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}