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
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039
040package java.rmi.activation;
041
042import gnu.java.rmi.server.ActivatableServerRef;
043import gnu.java.rmi.server.UnicastServer;
044import gnu.java.rmi.server.UnicastServerRef;
045
046import java.lang.reflect.Field;
047import java.rmi.MarshalledObject;
048import java.rmi.NoSuchObjectException;
049import java.rmi.Remote;
050import java.rmi.RemoteException;
051import java.rmi.server.ObjID;
052import java.rmi.server.RMIClientSocketFactory;
053import java.rmi.server.RMIServerSocketFactory;
054import java.rmi.server.RemoteObject;
055import java.rmi.server.RemoteServer;
056import 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 */
071public 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 incoming 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 incoming 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 incoming 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  private 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  private 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}