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