1 /* 2 * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 package java.rmi.server; 26 27 import java.io.InvalidObjectException; 28 import java.lang.reflect.InvocationHandler; 29 import java.lang.reflect.Method; 30 import java.lang.reflect.Proxy; 31 import java.rmi.Remote; 32 import java.rmi.UnexpectedException; 33 import java.rmi.activation.Activatable; 34 import java.util.Map; 35 import java.util.WeakHashMap; 36 import sun.rmi.server.Util; 37 import sun.rmi.server.WeakClassHashMap; 38 39 /** 40 * An implementation of the <code>InvocationHandler</code> interface for 41 * use with Java Remote Method Invocation (Java RMI). This invocation 42 * handler can be used in conjunction with a dynamic proxy instance as a 43 * replacement for a pregenerated stub class. 44 * 45 * <p>Applications are not expected to use this class directly. A remote 46 * object exported to use a dynamic proxy with {@link UnicastRemoteObject} 47 * or {@link Activatable} has an instance of this class as that proxy's 48 * invocation handler. 49 * 50 * @author Ann Wollrath 51 * @since 1.5 52 **/ 53 public class RemoteObjectInvocationHandler 54 extends RemoteObject 55 implements InvocationHandler 56 { 57 private static final long serialVersionUID = 2L; 58 59 /** 60 * A weak hash map, mapping classes to weak hash maps that map 61 * method objects to method hashes. 62 **/ 63 private static final MethodToHash_Maps methodToHash_Maps = 64 new MethodToHash_Maps(); 65 66 /** 67 * Creates a new <code>RemoteObjectInvocationHandler</code> constructed 68 * with the specified <code>RemoteRef</code>. 69 * 70 * @param ref the remote ref 71 * 72 * @throws NullPointerException if <code>ref</code> is <code>null</code> 73 **/ 74 public RemoteObjectInvocationHandler(RemoteRef ref) { 75 super(ref); 76 if (ref == null) { 77 throw new NullPointerException(); 78 } 79 } 80 81 /** 82 * Processes a method invocation made on the encapsulating 83 * proxy instance, <code>proxy</code>, and returns the result. 84 * 85 * <p><code>RemoteObjectInvocationHandler</code> implements this method 86 * as follows: 87 * 88 * <p>If <code>method</code> is one of the following methods, it 89 * is processed as described below: 90 * 91 * <ul> 92 * 93 * <li>{@link Object#hashCode Object.hashCode}: Returns the hash 94 * code value for the proxy. 95 * 96 * <li>{@link Object#equals Object.equals}: Returns <code>true</code> 97 * if the argument (<code>args[0]</code>) is an instance of a dynamic 98 * proxy class and this invocation handler is equal to the invocation 99 * handler of that argument, and returns <code>false</code> otherwise. 100 * 101 * <li>{@link Object#toString Object.toString}: Returns a string 102 * representation of the proxy. 103 * </ul> 104 * 105 * <p>Otherwise, a remote call is made as follows: 106 * 107 * <ul> 108 * <li>If <code>proxy</code> is not an instance of the interface 109 * {@link Remote}, then an {@link IllegalArgumentException} is thrown. 110 * 111 * <li>Otherwise, the {@link RemoteRef#invoke invoke} method is invoked 112 * on this invocation handler's <code>RemoteRef</code>, passing 113 * <code>proxy</code>, <code>method</code>, <code>args</code>, and the 114 * method hash (defined in section 8.3 of the "Java Remote Method 115 * Invocation (RMI) Specification") for <code>method</code>, and the 116 * result is returned. 117 * 118 * <li>If an exception is thrown by <code>RemoteRef.invoke</code> and 119 * that exception is a checked exception that is not assignable to any 120 * exception in the <code>throws</code> clause of the method 121 * implemented by the <code>proxy</code>'s class, then that exception 122 * is wrapped in an {@link UnexpectedException} and the wrapped 123 * exception is thrown. Otherwise, the exception thrown by 124 * <code>invoke</code> is thrown by this method. 125 * </ul> 126 * 127 * <p>The semantics of this method are unspecified if the 128 * arguments could not have been produced by an instance of some 129 * valid dynamic proxy class containing this invocation handler. 130 * 131 * @param proxy the proxy instance that the method was invoked on 132 * @param method the <code>Method</code> instance corresponding to the 133 * interface method invoked on the proxy instance 134 * @param args an array of objects containing the values of the 135 * arguments passed in the method invocation on the proxy instance, or 136 * <code>null</code> if the method takes no arguments 137 * @return the value to return from the method invocation on the proxy 138 * instance 139 * @throws Throwable the exception to throw from the method invocation 140 * on the proxy instance 141 * @see 142 **/ 143 public Object invoke(Object proxy, Method method, Object[] args) 144 throws Throwable 145 { 146 if (method.getDeclaringClass() == Object.class) { 147 return invokeObjectMethod(proxy, method, args); 148 } else { 149 return invokeRemoteMethod(proxy, method, args); 150 } 151 } 152 153 /** 154 * Handles java.lang.Object methods. 155 **/ 156 private Object invokeObjectMethod(Object proxy, 157 Method method, 158 Object[] args) 159 { 160 String name = method.getName(); 161 162 if (name.equals("hashCode")) { 163 return hashCode(); 164 165 } else if (name.equals("equals")) { 166 Object obj = args[0]; 167 return 168 proxy == obj || 169 (obj != null && 170 Proxy.isProxyClass(obj.getClass()) && 171 equals(Proxy.getInvocationHandler(obj))); 172 173 } else if (name.equals("toString")) { 174 return proxyToString(proxy); 175 176 } else { 177 throw new IllegalArgumentException( 178 "unexpected Object method: " + method); 179 } 180 } 181 182 /** 183 * Handles remote methods. 184 **/ 185 private Object invokeRemoteMethod(Object proxy, 186 Method method, 187 Object[] args) 188 throws Exception 189 { 190 try { 191 if (!(proxy instanceof Remote)) { 192 throw new IllegalArgumentException( 193 "proxy not Remote instance"); 194 } 195 return ref.invoke((Remote) proxy, method, args, 196 getMethodHash(method)); 197 } catch (Exception e) { 198 if (!(e instanceof RuntimeException)) { 199 Class<?> cl = proxy.getClass(); 200 try { 201 method = cl.getMethod(method.getName(), 202 method.getParameterTypes()); 203 } catch (NoSuchMethodException nsme) { 204 throw (IllegalArgumentException) 205 new IllegalArgumentException().initCause(nsme); 206 } 207 Class<?> thrownType = e.getClass(); 208 for (Class<?> declaredType : method.getExceptionTypes()) { 209 if (declaredType.isAssignableFrom(thrownType)) { 210 throw e; 211 } 212 } 213 e = new UnexpectedException("unexpected exception", e); 214 } 215 throw e; 216 } 217 } 218 219 /** 220 * Returns a string representation for a proxy that uses this invocation 221 * handler. 222 **/ 223 private String proxyToString(Object proxy) { 224 Class<?>[] interfaces = proxy.getClass().getInterfaces(); 225 if (interfaces.length == 0) { 226 return "Proxy[" + this + "]"; 227 } 228 String iface = interfaces[0].getName(); 229 if (iface.equals("java.rmi.Remote") && interfaces.length > 1) { 230 iface = interfaces[1].getName(); 231 } 232 int dot = iface.lastIndexOf('.'); 233 if (dot >= 0) { 234 iface = iface.substring(dot + 1); 235 } 236 return "Proxy[" + iface + "," + this + "]"; 237 } 238 239 /** 240 * @throws InvalidObjectException unconditionally 241 **/ 242 private void readObjectNoData() throws InvalidObjectException { 243 throw new InvalidObjectException("no data in stream; class: " + 244 this.getClass().getName()); 245 } 246 247 /** 248 * Returns the method hash for the specified method. Subsequent calls 249 * to "getMethodHash" passing the same method argument should be faster 250 * since this method caches internally the result of the method to 251 * method hash mapping. The method hash is calculated using the 252 * "computeMethodHash" method. 253 * 254 * @param method the remote method 255 * @return the method hash for the specified method 256 */ 257 private static long getMethodHash(Method method) { 258 return methodToHash_Maps.get(method.getDeclaringClass()).get(method); 259 } 260 261 /** 262 * A weak hash map, mapping classes to weak hash maps that map 263 * method objects to method hashes. 264 **/ 265 private static class MethodToHash_Maps 266 extends WeakClassHashMap<Map<Method,Long>> 267 { 268 MethodToHash_Maps() {} 269 270 protected Map<Method,Long> computeValue(Class<?> remoteClass) { 271 return new WeakHashMap<Method,Long>() { 272 public synchronized Long get(Object key) { 273 Long hash = super.get(key); 274 if (hash == null) { 275 Method method = (Method) key; 276 hash = Util.computeMethodHash(method); 277 put(method, hash); 278 } 279 return hash; 280 } 281 }; 282 } 283 } 284 }