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