001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.remoting.support;
018
019import java.io.Serializable;
020import java.lang.reflect.InvocationTargetException;
021import java.lang.reflect.Method;
022import java.util.HashMap;
023import java.util.Map;
024
025import org.aopalliance.intercept.MethodInvocation;
026
027import org.springframework.lang.Nullable;
028import org.springframework.util.ClassUtils;
029
030/**
031 * Encapsulates a remote invocation, providing core method invocation properties
032 * in a serializable fashion. Used for RMI and HTTP-based serialization invokers.
033 *
034 * <p>This is an SPI class, typically not used directly by applications.
035 * Can be subclassed for additional invocation parameters.
036 *
037 * <p>Both {@link RemoteInvocation} and {@link RemoteInvocationResult} are designed
038 * for use with standard Java serialization as well as JavaBean-style serialization.
039 *
040 * @author Juergen Hoeller
041 * @since 25.02.2004
042 * @see RemoteInvocationResult
043 * @see RemoteInvocationFactory
044 * @see RemoteInvocationExecutor
045 * @see org.springframework.remoting.rmi.RmiProxyFactoryBean
046 * @see org.springframework.remoting.rmi.RmiServiceExporter
047 * @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
048 * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
049 */
050public class RemoteInvocation implements Serializable {
051
052        /** use serialVersionUID from Spring 1.1 for interoperability. */
053        private static final long serialVersionUID = 6876024250231820554L;
054
055
056        private String methodName;
057
058        private Class<?>[] parameterTypes;
059
060        private Object[] arguments;
061
062        private Map<String, Serializable> attributes;
063
064
065        /**
066         * Create a new RemoteInvocation for the given AOP method invocation.
067         * @param methodInvocation the AOP invocation to convert
068         */
069        public RemoteInvocation(MethodInvocation methodInvocation) {
070                this.methodName = methodInvocation.getMethod().getName();
071                this.parameterTypes = methodInvocation.getMethod().getParameterTypes();
072                this.arguments = methodInvocation.getArguments();
073        }
074
075        /**
076         * Create a new RemoteInvocation for the given parameters.
077         * @param methodName the name of the method to invoke
078         * @param parameterTypes the parameter types of the method
079         * @param arguments the arguments for the invocation
080         */
081        public RemoteInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments) {
082                this.methodName = methodName;
083                this.parameterTypes = parameterTypes;
084                this.arguments = arguments;
085        }
086
087        /**
088         * Create a new RemoteInvocation for JavaBean-style deserialization
089         * (e.g. with Jackson).
090         */
091        public RemoteInvocation() {
092        }
093
094
095        /**
096         * Set the name of the target method.
097         * <p>This setter is intended for JavaBean-style deserialization.
098         */
099        public void setMethodName(String methodName) {
100                this.methodName = methodName;
101        }
102
103        /**
104         * Return the name of the target method.
105         */
106        public String getMethodName() {
107                return this.methodName;
108        }
109
110        /**
111         * Set the parameter types of the target method.
112         * <p>This setter is intended for JavaBean-style deserialization.
113         */
114        public void setParameterTypes(Class<?>[] parameterTypes) {
115                this.parameterTypes = parameterTypes;
116        }
117
118        /**
119         * Return the parameter types of the target method.
120         */
121        public Class<?>[] getParameterTypes() {
122                return this.parameterTypes;
123        }
124
125        /**
126         * Set the arguments for the target method call.
127         * <p>This setter is intended for JavaBean-style deserialization.
128         */
129        public void setArguments(Object[] arguments) {
130                this.arguments = arguments;
131        }
132
133        /**
134         * Return the arguments for the target method call.
135         */
136        public Object[] getArguments() {
137                return this.arguments;
138        }
139
140
141        /**
142         * Add an additional invocation attribute. Useful to add additional
143         * invocation context without having to subclass RemoteInvocation.
144         * <p>Attribute keys have to be unique, and no overriding of existing
145         * attributes is allowed.
146         * <p>The implementation avoids to unnecessarily create the attributes
147         * Map, to minimize serialization size.
148         * @param key the attribute key
149         * @param value the attribute value
150         * @throws IllegalStateException if the key is already bound
151         */
152        public void addAttribute(String key, Serializable value) throws IllegalStateException {
153                if (this.attributes == null) {
154                        this.attributes = new HashMap<>();
155                }
156                if (this.attributes.containsKey(key)) {
157                        throw new IllegalStateException("There is already an attribute with key '" + key + "' bound");
158                }
159                this.attributes.put(key, value);
160        }
161
162        /**
163         * Retrieve the attribute for the given key, if any.
164         * <p>The implementation avoids to unnecessarily create the attributes
165         * Map, to minimize serialization size.
166         * @param key the attribute key
167         * @return the attribute value, or {@code null} if not defined
168         */
169        @Nullable
170        public Serializable getAttribute(String key) {
171                if (this.attributes == null) {
172                        return null;
173                }
174                return this.attributes.get(key);
175        }
176
177        /**
178         * Set the attributes Map. Only here for special purposes:
179         * Preferably, use {@link #addAttribute} and {@link #getAttribute}.
180         * @param attributes the attributes Map
181         * @see #addAttribute
182         * @see #getAttribute
183         */
184        public void setAttributes(@Nullable Map<String, Serializable> attributes) {
185                this.attributes = attributes;
186        }
187
188        /**
189         * Return the attributes Map. Mainly here for debugging purposes:
190         * Preferably, use {@link #addAttribute} and {@link #getAttribute}.
191         * @return the attributes Map, or {@code null} if none created
192         * @see #addAttribute
193         * @see #getAttribute
194         */
195        @Nullable
196        public Map<String, Serializable> getAttributes() {
197                return this.attributes;
198        }
199
200
201        /**
202         * Perform this invocation on the given target object.
203         * Typically called when a RemoteInvocation is received on the server.
204         * @param targetObject the target object to apply the invocation to
205         * @return the invocation result
206         * @throws NoSuchMethodException if the method name could not be resolved
207         * @throws IllegalAccessException if the method could not be accessed
208         * @throws InvocationTargetException if the method invocation resulted in an exception
209         * @see java.lang.reflect.Method#invoke
210         */
211        public Object invoke(Object targetObject)
212                        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
213
214                Method method = targetObject.getClass().getMethod(this.methodName, this.parameterTypes);
215                return method.invoke(targetObject, this.arguments);
216        }
217
218
219        @Override
220        public String toString() {
221                return "RemoteInvocation: method name '" + this.methodName + "'; parameter types " +
222                                ClassUtils.classNamesToString(this.parameterTypes);
223        }
224
225}