1 /* UnicastServerRef.java -- 2 Copyright (c) 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2006 3 Free Software Foundation, Inc. 4 5 This file is part of GNU Classpath. 6 7 GNU Classpath is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 GNU Classpath is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GNU Classpath; see the file COPYING. If not, write to the 19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 02110-1301 USA. 21 22 Linking this library statically or dynamically with other modules is 23 making a combined work based on this library. Thus, the terms and 24 conditions of the GNU General Public License cover the whole 25 combination. 26 27 As a special exception, the copyright holders of this library give you 28 permission to link this library with independent modules to produce an 29 executable, regardless of the license terms of these independent 30 modules, and to copy and distribute the resulting executable under 31 terms of your choice, provided that you also meet, for each linked 32 independent module, the terms and conditions of the license of that 33 module. An independent module is a module which is not derived from 34 or based on this library. If you modify this library, you may extend 35 this exception to your version of the library, but you are not 36 obligated to do so. If you do not wish to do so, delete this 37 exception statement from your version. */ 38 39 40 package gnu.java.rmi.server; 41 42 import java.io.ObjectInputStream; 43 import java.lang.reflect.Constructor; 44 import java.lang.reflect.InvocationTargetException; 45 import java.lang.reflect.Method; 46 import java.lang.reflect.Proxy; 47 import java.rmi.Remote; 48 import java.rmi.RemoteException; 49 import java.rmi.server.ObjID; 50 import java.rmi.server.RMIServerSocketFactory; 51 import java.rmi.server.RemoteObjectInvocationHandler; 52 import java.rmi.server.RemoteRef; 53 import java.rmi.server.RemoteServer; 54 import java.rmi.server.RemoteStub; 55 import java.rmi.server.ServerNotActiveException; 56 import java.rmi.server.Skeleton; 57 import java.util.HashSet; 58 import java.util.Hashtable; 59 import java.util.Iterator; 60 61 /** 62 * This class connects the local, remotely available (exported) object to 63 * the local RMI server that accepts the remote calls. 64 */ 65 public class UnicastServerRef 66 extends UnicastRef 67 { 68 69 /** 70 * Use GNU Classpath v 0.20 SVUID for interoperability 71 */ 72 private static final long serialVersionUID = - 5585608108300801246L; 73 74 /** 75 * The class array, defining parameters of the jdk 1.2 RMI stub constructor. 76 */ 77 private static final Class[] stubprototype = new Class[] { RemoteRef.class }; 78 79 /** 80 * The exported remote object itself. 81 */ 82 Remote myself; // save the remote object itself 83 84 /** 85 * The skeleton (if any), associated with the exported remote object. 86 */ 87 protected Skeleton skel; 88 89 /** 90 * The stub, associated with the exported remote object (may be proxy class). 91 */ 92 protected Remote stub; 93 94 /** 95 * The method table (RMI hash code to method) of the methods of the 96 * exported object. 97 */ 98 protected Hashtable methods = new Hashtable(); 99 100 /** 101 * Used by serialization. 102 */ UnicastServerRef()103 UnicastServerRef() 104 { 105 } 106 UnicastServerRef(ObjID id, int port, RMIServerSocketFactory ssf)107 public UnicastServerRef(ObjID id, int port, RMIServerSocketFactory ssf) 108 throws RemoteException 109 { 110 super(id); 111 manager = UnicastConnectionManager.getInstance(port, ssf); 112 } 113 114 /** 115 * Export the object and return its remote stub. The method tries to locate 116 * existing stubs and skeletons. If this fails, the method instantiates the 117 * proxy stub class. 118 * 119 * Stubs and skeletons are always ignored (even if present) if the 120 * java.rmi.server.ignoreStubClasses property is set to true. 121 * 122 * @param obj the object being exported. 123 * @return the stub (existing class or proxy) of the exported object. 124 * @throws RemoteException if the export failed due any reason 125 */ exportObject(Remote obj)126 public Remote exportObject(Remote obj) throws RemoteException 127 { 128 if (myself == null) 129 { 130 myself = obj; 131 // Save it to server manager, to let client calls in the same VM to 132 // issue local call 133 manager.serverobj = obj; 134 135 String ignoreStubs; 136 137 ClassLoader loader =obj.getClass().getClassLoader(); 138 139 // Stubs are always searched for the bootstrap classes that may have 140 // obsolete pattern and may still need also skeletons. 141 if (loader==null) 142 ignoreStubs = "false"; 143 else 144 ignoreStubs = System.getProperty("java.rmi.server.ignoreStubClasses", 145 "false"); 146 147 if (! ignoreStubs.equals("true")) 148 { 149 // Find and install the stub 150 Class cls = obj.getClass(); 151 152 // where ist the _Stub? (check superclasses also) 153 Class expCls = findStubSkelClass(cls); 154 155 if (expCls != null) 156 { 157 stub = (RemoteStub) getHelperClass(expCls, "_Stub"); 158 // Find and install the skeleton (if there is one) 159 skel = (Skeleton) getHelperClass(expCls, "_Skel"); 160 } 161 } 162 163 if (stub == null) 164 stub = createProxyStub(obj.getClass(), this); 165 166 // Build hash of methods which may be called. 167 buildMethodHash(obj.getClass(), true); 168 169 // Export it. 170 UnicastServer.exportObject(this); 171 } 172 173 return stub; 174 } 175 176 /** 177 * Get the stub (actual class or proxy) of the exported remote object. 178 * 179 * @return the remote stub (null if exportObject has not been called). 180 */ getStub()181 public Remote getStub() 182 { 183 return stub; 184 } 185 186 /** 187 * Unexport the object (remove methods from the method hashcode table 188 * and call UnicastServer.unexportObject. 189 * 190 * @param obj the object being unexported 191 * @param force passed to the UnicastServer.unexportObject. 192 * @return value, returned by the UnicastServer.unexportObject. 193 */ unexportObject(Remote obj, boolean force)194 public boolean unexportObject(Remote obj, boolean force) 195 { 196 // Remove all hashes of methods which may be called. 197 buildMethodHash(obj.getClass(), false); 198 return UnicastServer.unexportObject(this, force); 199 } 200 201 /** 202 * Return the class in the hierarchy for that the stub class is defined. 203 * The Subs/Skels might not there for the actual class, but maybe for one of 204 * the superclasses. 205 * 206 * @return the class having stub defined, null if none. 207 */ findStubSkelClass(Class startCls)208 protected Class findStubSkelClass(Class startCls) 209 { 210 Class cls = startCls; 211 212 while (true) 213 { 214 try 215 { 216 String stubClassname = cls.getName() + "_Stub"; 217 ClassLoader cl = cls.getClassLoader(); 218 Class scls = cl == null ? Class.forName(stubClassname) 219 : cl.loadClass(stubClassname); 220 return cls; // found it 221 } 222 catch (ClassNotFoundException e) 223 { 224 Class superCls = cls.getSuperclass(); 225 if (superCls == null 226 || superCls == java.rmi.server.UnicastRemoteObject.class) 227 { 228 return null; 229 } 230 cls = superCls; 231 } 232 } 233 } 234 235 /** 236 * Get the helper (assisting) class with the given type. 237 * 238 * @param cls the class, for that the helper class is requested. This class 239 * and the requested helper class must share the same class loader. 240 * 241 * @param type the type of the assisting helper. The only currently supported 242 * non deprecated value is "_Stub" (load jdk 1.1 or 1.2 RMI stub). Another 243 * (deprecated) value is "_Skel" (load skeleton). 244 * 245 * @return the instantiated instance of the helper class or null if the 246 * helper class cannot be found or instantiated. 247 */ getHelperClass(Class cls, String type)248 protected Object getHelperClass(Class cls, String type) 249 { 250 try 251 { 252 String classname = cls.getName(); 253 ClassLoader cl = cls.getClassLoader(); 254 Class scls = cl == null ? Class.forName(classname + type) 255 : cl.loadClass(classname + type); 256 if (type.equals("_Stub")) 257 { 258 try 259 { 260 // JDK 1.2 stubs 261 Constructor con = scls.getConstructor(stubprototype); 262 return (con.newInstance(new Object[] { this })); 263 } 264 catch (NoSuchMethodException e) 265 { 266 } 267 catch (InstantiationException e) 268 { 269 } 270 catch (IllegalAccessException e) 271 { 272 } 273 catch (IllegalArgumentException e) 274 { 275 } 276 catch (InvocationTargetException e) 277 { 278 } 279 // JDK 1.1 stubs 280 RemoteStub stub = (RemoteStub) scls.newInstance(); 281 UnicastRemoteStub.setStubRef(stub, this); 282 return (stub); 283 } 284 else 285 { 286 // JDK 1.1 skel 287 return (scls.newInstance()); 288 } 289 } 290 catch (ClassNotFoundException e) 291 { 292 } 293 catch (InstantiationException e) 294 { 295 } 296 catch (IllegalAccessException e) 297 { 298 } 299 return (null); 300 } 301 getClientHost()302 public String getClientHost() throws ServerNotActiveException 303 { 304 return RemoteServer.getClientHost(); 305 } 306 307 /** 308 * Build the method has code table and put it into {@link #methods} 309 * (mapping RMI hashcode tos method). The same method is used to remove 310 * the table. 311 * 312 * @param cls the class for that the method table is built. 313 * @param build if true, the class methods are added to the table. If 314 * false, they are removed from the table. 315 */ buildMethodHash(Class cls, boolean build)316 protected void buildMethodHash(Class cls, boolean build) 317 { 318 Method[] meths = cls.getMethods(); 319 for (int i = 0; i < meths.length; i++) 320 { 321 /* Don't need to include any java.xxx related stuff */ 322 if (meths[i].getDeclaringClass().getName().startsWith("java.")) 323 { 324 continue; 325 } 326 long hash = RMIHashes.getMethodHash(meths[i]); 327 if (build) 328 methods.put(new Long(hash), meths[i]); 329 else 330 methods.remove(new Long(hash)); 331 // System.out.println("meth = " + meths[i] + ", hash = " + hash); 332 } 333 } 334 getMethodReturnType(int method, long hash)335 Class getMethodReturnType(int method, long hash) throws Exception 336 { 337 if (method == - 1) 338 { 339 Method meth = (Method) methods.get(new Long(hash)); 340 return meth.getReturnType(); 341 } 342 else 343 return null; 344 } 345 346 /** 347 * This method is called from the {@link UnicastServer#incomingMessageCall} 348 * to deliver the remote call to this object. 349 */ incomingMessageCall(UnicastConnection conn, int method, long hash)350 public Object incomingMessageCall(UnicastConnection conn, int method, 351 long hash) throws Exception 352 { 353 // System.out.println("method = " + method + ", hash = " + hash); 354 // If method is -1 then this is JDK 1.2 RMI - so use the hash 355 // to locate the method 356 if (method == - 1) 357 { 358 Method meth = (Method) methods.get(new Long(hash)); 359 // System.out.println("class = " + myself.getClass() + ", meth = " + 360 // meth); 361 if (meth == null) 362 { 363 throw new NoSuchMethodException( 364 myself.getClass().getName()+" hash "+hash); 365 } 366 367 ObjectInputStream in = conn.getObjectInputStream(); 368 int nrargs = meth.getParameterTypes().length; 369 Object[] args = new Object[nrargs]; 370 for (int i = 0; i < nrargs; i++) 371 { 372 /** 373 * For debugging purposes - we don't handle CodeBases quite right so 374 * we don't always find the stubs. This lets us know that. 375 */ 376 try 377 { 378 // need to handle primitive types 379 args[i] = ((RMIObjectInputStream) in) 380 .readValue(meth.getParameterTypes()[i]); 381 382 } 383 catch (Exception t) 384 { 385 t.printStackTrace(); 386 throw t; 387 } 388 } 389 //We must reinterpret the exception thrown by meth.invoke() 390 //return (meth.invoke(myself, args)); 391 Object ret = null; 392 try 393 { 394 ret = meth.invoke(myself, args); 395 } 396 catch (InvocationTargetException e) 397 { 398 Throwable cause = e.getTargetException(); 399 if (cause instanceof Exception) 400 { 401 throw (Exception) cause; 402 } 403 else if (cause instanceof Error) 404 { 405 throw (Error) cause; 406 } 407 else 408 { 409 throw new Error( 410 "The remote method threw a java.lang.Throwable that"+ 411 " is neither java.lang.Exception nor java.lang.Error.", 412 e); 413 } 414 } 415 return ret; 416 } 417 // Otherwise this is JDK 1.1 style RMI - we find the skeleton 418 // and invoke it using the method number. We wrap up our 419 // connection system in a UnicastRemoteCall so it appears in a 420 // way the Skeleton can handle. 421 else 422 { 423 if (skel == null) 424 throw new NoSuchMethodException("JDK 1.1 call - Skeleton required"); 425 426 UnicastRemoteCall call = new UnicastRemoteCall(conn); 427 skel.dispatch(myself, call, method, hash); 428 if (! call.isReturnValue()) 429 return RMIVoidValue.INSTANCE; 430 else 431 return (call.returnValue()); 432 } 433 } 434 435 /** 436 * Create the 1.2 proxy stub in the case when the pre-generated stub is not 437 * available of the system is explicitly instructed to use proxy stubs. 438 * 439 * @param stubFor the class for that the proxy class must be constructed. 440 * @param reference the remote reference, used to find the given object 441 * 442 * @return the applicable proxy stub. 443 * 444 * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) 445 */ createProxyStub(Class stubFor, RemoteRef reference)446 Remote createProxyStub(Class stubFor, RemoteRef reference) 447 { 448 // Collect all interfaces, implemented by stubFor and derived from 449 // Remote (also Remote itself): 450 HashSet interfaces = new HashSet(); 451 Class c = stubFor; 452 Class[] intfs; 453 454 while (c != null) 455 { 456 intfs = c.getInterfaces(); 457 for (int i = 0; i < intfs.length; i++) 458 { 459 if (Remote.class.isAssignableFrom(intfs[i])) 460 interfaces.add(intfs[i]); 461 } 462 c = c.getSuperclass(); 463 } 464 465 intfs = new Class[interfaces.size()]; 466 Iterator it = interfaces.iterator(); 467 468 for (int i = 0; i < intfs.length; i++) 469 intfs[i] = (Class) it.next(); 470 471 RemoteObjectInvocationHandler handler = 472 new RemoteObjectInvocationHandler(reference); 473 474 Object proxy = 475 Proxy.newProxyInstance(stubFor.getClassLoader(), intfs, handler); 476 477 return (Remote) proxy; 478 } 479 480 481 } 482