001    /* Activatable.java -- A common ancestor for the activatable objects.
002       Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011     
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.rmi.activation;
041    
042    import gnu.java.rmi.server.ActivatableServerRef;
043    import gnu.java.rmi.server.UnicastServer;
044    import gnu.java.rmi.server.UnicastServerRef;
045    
046    import java.lang.reflect.Field;
047    import java.rmi.MarshalledObject;
048    import java.rmi.NoSuchObjectException;
049    import java.rmi.Remote;
050    import java.rmi.RemoteException;
051    import java.rmi.server.ObjID;
052    import java.rmi.server.RMIClientSocketFactory;
053    import java.rmi.server.RMIServerSocketFactory;
054    import java.rmi.server.RemoteObject;
055    import java.rmi.server.RemoteServer;
056    import java.rmi.server.UnicastRemoteObject;
057    
058    /**
059     * A common ancestor for the implementations of the activatable objects. Such
060     * objects require persistent access over time and can be activated by the
061     * system. The derived classes also implements the needed interface of some
062     * remote object and usually have the two parameter constructor, the first
063     * parameter being the {@link ActivationID} and the second the
064     * {@link MarshalledObject}. Activatable is the main class that developers need
065     * to use to implement and manage activatable objects. It also contains methods
066     * for making activatable remote objects that are not derived from the 
067     * Activatable class.
068     * 
069     * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub) 
070     */
071    public abstract class Activatable
072        extends RemoteServer
073    {
074    
075      /**
076       * Use SVUID for interoperability.
077       */
078      static final long serialVersionUID = - 3120617863591563455L;
079      
080      /**
081       * The object activation id.
082       */
083      final ActivationID id;
084      
085      /**
086       * This constructor is used to register export the object on the given port. A
087       * subclass of the Activatable class calls this constructor to register and
088       * export the object during initial construction. As a side-effect of
089       * activatable object construction, the remote object is both "registered"
090       * with the activation system and "exported" (on an anonymous port, if port is
091       * zero) to the RMI runtime so that it is available to accept incoming calls
092       * from clients.
093       * 
094       * @param codebase the object code base url
095       * @param data the data, needed to activate the object.
096       * @param restart specifies reactivation mode after crash. If true, the object
097       *          is activated when activator is restarted or the activation group
098       *          is restarted. If false, the object is only activated on demand.
099       *          This flag does has no effect during the normal operation (the
100       *          object is normally activated on demand).
101       * @param port the port, on which the object will become available. The value
102       *          0 means anonymous port.
103       * @throws ActivationException if the activation failed
104       * @throws RemoteException if the remote call failed.
105       */
106      protected Activatable(String codebase, MarshalledObject<?> data,
107                            boolean restart, int port) throws ActivationException,
108          RemoteException
109      {
110        ActivationDesc descriptor = new ActivationDesc(getClass().getName(),
111                                                       codebase, data, restart);
112        id = obtainId(descriptor);
113        exportObject(this, id, port);
114      }
115    
116      /**
117       * This constructor is used to register export the object on the given port,
118       * additionally specifying the socket factories. A subclass of the Activatable
119       * class calls this constructor to register and export the object during
120       * initial construction.
121       * 
122       * @param codebase the object code base url
123       * @param data the data, needed to activate the object.
124       * @param restart specifies reactivation mode after crash. If true, the object
125       *          is activated when activator is restarted or the activation group
126       *          is restarted. If false, the object is only activated on demand.
127       *          This flag does has no effect during the normal operation (the
128       *          object is normally activated on demand).
129       * @param port the port, on which the object will become available. The value
130       *          0 means anonymous port.
131       * @param csf the client socket factory
132       * @param ssf the server socket factory
133       * @throws ActivationException if the activation failed
134       * @throws RemoteException if the remote call failed.
135       */
136      protected Activatable(String codebase, MarshalledObject<?> data,
137                            boolean restart, int port, RMIClientSocketFactory csf,
138                            RMIServerSocketFactory ssf) throws ActivationException,
139          RemoteException
140      {
141        ActivationDesc descriptor = new ActivationDesc(getClass().getName(),
142                                                       codebase, data, restart);
143        id = obtainId(descriptor);
144        exportObject(this, id, port);
145      }
146    
147      /**
148       * Creates the new instance of activatable with the given activation id and is
149       * listening at the given port. A subclass of the Activatable class calls this
150       * constructor when the object itself is activated via its special
151       * "activation" constructor with the two parameters ({@link ActivationID},
152       * {@link MarshalledObject}). As a side effect, the object is exported and is
153       * available to accept incomming calls.
154       * 
155       * @param anId the activation id
156       * @param port the port, on which the activatable will be listening
157       * @throws RemoteException if the activation failed.
158       */
159      protected Activatable(ActivationID anId, int port) throws RemoteException
160      {
161        id = anId;
162        try
163          {
164            exportObject(this, anId, port);
165          }
166        catch (Exception e)
167          {
168            e.printStackTrace();
169            RemoteException acex = 
170              new RemoteException("cannot export Activatable", e);
171            throw acex;
172          }
173      }
174    
175      /**
176       * Creates the new instance of activatable with the given activation id and is
177       * listening at the given port, using the specified client and server sockets
178       * factories. A subclass of the Activatable class calls this
179       * constructor when the object itself is activated via its special
180       * "activation" constructor with the two parameters ({@link ActivationID},
181       * {@link MarshalledObject}). As a side effect, the object is exported and is
182       * available to accept incomming calls.
183       * 
184       * @param anId the activation id
185       * @param port the port, on which the activatable will be listening
186       * @param csf the client socket factory
187       * @param ssf the server socket factory
188       * 
189       * @throws RemoteException if the remote call failed
190       */
191      protected Activatable(ActivationID anId, int port, RMIClientSocketFactory csf,
192                            RMIServerSocketFactory ssf) throws RemoteException
193      {
194        id = anId;
195        try
196          {
197            exportObject(this, anId, port, csf, ssf);
198          }
199        catch (Exception e)
200          {
201            RemoteException acex = new RemoteException();
202            acex.initCause(e);
203            throw acex;
204          }
205      }
206      
207      /**
208       * Get the objects activation identifier.
209       * 
210       * @return the object activation identifier
211       */
212      protected ActivationID getID()
213      {
214        return id;
215      }
216      
217      /**
218       * Obtain the activation Id from the activation descriptor by registering
219       * within the current group.
220       */
221      static ActivationID obtainId(ActivationDesc descriptor)
222          throws RemoteException, UnknownGroupException, ActivationException
223      {
224        ActivationGroupID id = descriptor.getGroupID();
225        ActivationSystem system;
226    
227        if (id != null)
228          system = id.getSystem();
229        else
230          system = ActivationGroup.currentGroupID().getSystem();
231        return system.registerObject(descriptor);
232      }
233      
234      /**
235       * This method registers an activatable object. The object is expected to be
236       * on the anonymous port (null client and server socket factories).
237       * 
238       * @param desc the object description.
239       * @return the remote stub for the activatable object (the first call on this
240       *         stub will activate the object).
241       * @throws UnknownGroupException if the object group identifier is unknown
242       * @throws ActivationException if the activation system is not running
243       * @throws RemoteException if the remote call fails
244       */
245      public static Remote register(ActivationDesc desc)
246          throws UnknownGroupException, ActivationException, RemoteException
247      {
248        ActivationID id = obtainId(desc);
249        try
250          {
251            return toStub(
252                          id,
253                          Thread.currentThread().getContextClassLoader().loadClass(
254                            desc.getClassName()));
255          }
256        catch (ClassNotFoundException e)
257          {
258            throw new ActivationException("Class not found: "+desc.getClassName());
259          }
260      }
261      
262      /**
263       * Inactivates and unexports the object. The subsequent calls will activate
264       * the object again. The object is not inactivated if it is currently
265       * executing calls.
266       * 
267       * @param id the id of the object being inactivated
268       * @return true if the object has been inactivated, false if it has not been
269       *         inactivated because of the running or pending calls.
270       * @throws UnknownObjectException if the object is unknown.
271       * @throws ActivationException if the object group is not active
272       * @throws RemoteException if the remote call fails
273       */
274      public static boolean inactive(ActivationID id)
275          throws UnknownObjectException, ActivationException, RemoteException
276      {
277        if (id.group!=null)
278          id.group.inactiveObject(id);
279        return UnicastRemoteObject.unexportObject(id.activate(false), false);
280      }
281      
282      /**
283       * Unregister the object (the object will no longer be activable with that id)
284       * 
285       * @param id the object id
286       * @throws UnknownObjectException if the id is unknown
287       * @throws ActivationException if the activation system is not running
288       * @throws RemoteException if the remote call fails.
289       */
290      public static void unregister(ActivationID id) throws UnknownObjectException,
291          ActivationException, RemoteException
292      {
293        ActivationGroup.currentGroupId.getSystem().unregisterObject(id);
294        UnicastServer.unregisterActivatable(id);
295      }
296      
297      /**
298       * Register and export the object that activatable object that is not derived
299       * from the Activatable super class. It creates and registers the object
300       * activation descriptor. There is no need to call this method if the object
301       * extends Activable, as its work is done in the constructor
302       * {@link #Activatable(String, MarshalledObject, boolean, int)}.
303       * 
304       * @param obj the object, that is exported, becoming available at the given
305       *          port.
306       * @param location the object code location (codebase).
307       * @param data the data, needed to activate the object
308       * @param restart the restart mode
309       * @param port the port, where the object will be available
310       * 
311       * @return the created object activation ID.
312       * 
313       * @throws ActivationException if the activation group is not active
314       * @throws RemoteException if the registration or export fails
315       */
316      public static ActivationID exportObject(Remote obj, String location,
317                                              MarshalledObject<?> data,
318                                              boolean restart, int port)
319          throws ActivationException, RemoteException
320      {
321        ActivationDesc descriptor = new ActivationDesc(obj.getClass().getName(),
322                                                       location, data, restart);
323        ActivationID id = obtainId(descriptor);
324        Remote stub = exportObject(obj, id, port);    
325        return id;
326      }
327    
328      /**
329       * Register and export the object that activatable object that is not derived
330       * from the Activatable super class. It creates and registers the object
331       * activation descriptor. There is no need to call this method if the object
332       * extends Activable, as its work is done in the constructor
333       * {@link #Activatable(String, MarshalledObject, boolean, int, RMIClientSocketFactory, RMIServerSocketFactory)}
334       * 
335       * @param obj the object, that is exported, becoming available at the given
336       *          port.
337       * @param location the object code location (codebase).
338       * @param data the data, needed to activate the object
339       * @param restart the restart mode
340       * @param port the port, where the object will be available
341       * @param csf the client socket factory
342       * @param ssf the server socket factory
343       * 
344       * @return the created object activation ID.
345       * 
346       * @throws ActivationException if the activation group is not active
347       * @throws RemoteException if the registration or export fails
348       */
349      public static ActivationID exportObject(Remote obj, String location,
350                                              MarshalledObject data,
351                                              boolean restart, int port,
352                                              RMIClientSocketFactory csf,
353                                              RMIServerSocketFactory ssf)
354          throws ActivationException, RemoteException
355      {
356        ActivationDesc descriptor = new ActivationDesc(obj.getClass().getName(),
357                                                       location, data, restart);
358        ActivationID id = obtainId(descriptor);
359        Remote stub = exportObject(obj, id, port, csf, ssf);    
360        return id;
361    
362      }
363    
364      /**
365       * During activation, this exportObject method should be invoked explicitly by
366       * the activatable object, that does is not derived from the Activatable
367       * class. There is no need to call this method if the object extends
368       * Activable, as its work is done in the constructor
369       * {@link #Activatable(ActivationID, int)}
370       * 
371       * @param obj the object
372       * @param id the known activation id
373       * @param port the object port
374       *  
375       * @return the remote stub of the activatable object
376       * 
377       * @throws RemoteException if the object export fails
378       */
379      public static Remote exportObject(Remote obj, ActivationID id, int port)
380          throws RemoteException
381      {
382        Remote stub = export(id, obj, port, null);
383        return stub;
384      }
385    
386      /**
387       * During activation, this exportObject method should be invoked explicitly by
388       * the activatable object, that does is not derived from the Activatable
389       * class. There is no need to call this method if the object extends
390       * Activable, as its work is done in the constructor
391       * {@link #Activatable(ActivationID, int)}
392       * 
393       * @param obj the object
394       * @param id the known activation id
395       * @param port the object port
396       * @param csf the client socket factory
397       * @param ssf the server socket factory
398       *  
399       * @return the remote stub of the activatable object
400       * 
401       * @throws RemoteException if the object export fails
402       */
403      public static Remote exportObject(Remote obj, ActivationID id, int port,
404                                        RMIClientSocketFactory csf,
405                                        RMIServerSocketFactory ssf)
406          throws RemoteException
407      {
408        Remote stub = export(id, obj, port, ssf); 
409        return stub;
410    
411      }
412    
413      /**
414       * Make the remote object unavailable for incoming calls. This method also
415       * unregisters the object, so it cannot be activated again by incomming call
416       * (unless registered).
417       * 
418       * @param obj the object to unexport
419       * @param force if true, cancel all pending or running calls to that object
420       *          (if false, the object with such calls is not unexported and false
421       *          is returned by this method).
422       * @return if the object was successfully unexported, false otherwise
423       * @throws NoSuchObjectException if such object is not known
424       */
425      public static boolean unexportObject(Remote obj, boolean force)
426          throws NoSuchObjectException
427      {
428        Object aref = UnicastServer.getExportedRef(obj);
429        
430        // Unregister it also (otherwise will be activated during the subsequent
431        // call.
432        if (aref instanceof ActivatableServerRef)
433          {
434            ActivatableServerRef aar = (ActivatableServerRef) aref;
435            UnicastServer.unregisterActivatable(aar.actId);
436          }
437        return UnicastRemoteObject.unexportObject(obj, force);
438      }
439      
440      static Remote exportObject(Remote obj, int port, 
441                                 RMIServerSocketFactory serverSocketFactory) 
442        throws RemoteException
443      {
444        UnicastServerRef sref = null;
445        if (obj instanceof RemoteObject)
446          sref = (UnicastServerRef) ((RemoteObject) obj).getRef();
447    
448        if (sref == null)
449          sref = new UnicastServerRef(new ObjID(), port, serverSocketFactory);
450    
451        Remote stub = sref.exportObject(obj);
452        // addStub(obj, stub); 
453        // TODO Need to change the place of the stub repository
454        return stub;
455      }
456      
457      /**
458       * Create and export the new remote object, making it available at the given
459       * port, using sockets, produced by the specified factories.
460       * 
461       * @param port the port, on that the object should become available. Zero
462       *          means anonymous port.
463       * @param serverSocketFactory the server socket factory
464       */
465      public static Remote export(ActivationID id, Remote obj, int port,
466                                  RMIServerSocketFactory serverSocketFactory)
467          throws RemoteException
468      {
469        ActivatableServerRef sref = null;
470        sref = new ActivatableServerRef(makeId(id), id, port, serverSocketFactory);
471        return sref.exportObject(obj);
472      }  
473      
474      /**
475       * Make the object ID from the activation ID. The same activation ID always
476       * produces the identical object id.
477       * 
478       * @param aid the activation id
479       * 
480       * @return the object id
481       */
482      public static ObjID makeId(ActivationID aid)
483      {
484        ObjID id = new ObjID(0);
485        
486        // The fields of both ObjID and ActivationID must be package private,
487        // so we need to use the reflection to access them anyway.
488        // Probably other implementations use some very different approach.
489        
490        try
491          {
492            Field idUid =  ObjID.class.getDeclaredField("space");
493            Field aidUid = ActivationID.class.getDeclaredField("uid");
494            
495            aidUid.setAccessible(true);
496            idUid.setAccessible(true);
497            
498            idUid.set(id, aidUid.get(aid));
499          }
500        catch (Exception e)
501          {
502            InternalError ierr = new InternalError("Unable to set UID field");
503            ierr.initCause(e);
504            throw ierr;
505          }
506        
507        return id;
508      }  
509      
510      /**
511       * Connect the object to the UnicastServer (export), but not activate it.
512       * The object will be activated on the first call.
513       */
514      static Remote toStub(ActivationID anId, Class stubFor)
515      {
516        try
517          {
518            ActivatableServerRef asr = 
519              new ActivatableServerRef(makeId(anId), anId, 0, null);
520            UnicastServer.exportActivatableObject(asr);
521            return asr.exportClass(stubFor);
522          }
523        catch (RemoteException e)
524          {
525            InternalError ierr = new InternalError(
526              "Failed to obtain activatable stub");
527            ierr.initCause(e);
528            throw ierr;
529          }
530      }
531    }