1 /*------------------------------------------------------------------------------
  2 Name:      RmiDriver.java
  3 Project:   xmlBlaster.org
  4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
  5 Comment:   RmiDriver class to invoke the xmlBlaster server using RMI.
  6 Version:   $Id: RmiDriver.java 15205 2006-06-29 17:25:12Z ruff $
  7 ------------------------------------------------------------------------------*/
  8 package org.xmlBlaster.protocol.rmi;
  9 
 10 import java.util.logging.Logger;
 11 import java.util.logging.Level;
 12 import org.xmlBlaster.util.Global;
 13 import org.xmlBlaster.util.XmlBlasterException;
 14 import org.xmlBlaster.util.def.Constants;
 15 import org.xmlBlaster.util.def.ErrorCode;
 16 import org.xmlBlaster.util.XmlBlasterSecurityManager;
 17 import org.xmlBlaster.engine.qos.AddressServer;
 18 import org.xmlBlaster.protocol.I_Authenticate;
 19 import org.xmlBlaster.protocol.I_XmlBlaster;
 20 import org.xmlBlaster.protocol.I_Driver;
 21 
 22 import java.rmi.RemoteException;
 23 import java.rmi.Naming;
 24 import java.rmi.AlreadyBoundException;
 25 
 26 /**
 27  * RmiDriver class to invoke the xmlBlaster server using RMI.
 28  * <p />
 29  * Design issues:
 30  * <p />
 31  * How to identify the calling client.
 32  * <p />
 33  * How does RMI handle incoming requests?<br />
 34  * Is it a worker thread model, or a 1 client/one thread
 35  * model, or 1 worker thread per RMI call?
 36  * When a call comes in a random thread is taken from a pool.<br />
 37  * According to the rmi specs "A method dispatched by the RMI runtime to a
 38  * remote object implementation (a server) may or may not execute in a
 39  * separate thread. Some calls originating from the same client virtual
 40  * machine will execute in the same thread; some will execute in different
 41  * threads. Calls originating from different client virtual machines will
 42  * execute in different threads. Other than this last case of different
 43  * client virtual machines, the RMI runtime makes no guarantees with
 44  * respect to mapping remote object invocations to threads. "<br />
 45  * <p />
 46  * Possible soultions:
 47  * <ul>
 48  *    <li>Give each client its own remote object to call<br />
 49  *        We can implicitly identiofy the client, but we
 50  *        will have problems with many connecting clients
 51  *        because of the 1-1 mapping between threads and sockets.
 52  *    </li>
 53  *    <li>Pass the sessionId with each method call<br />
 54  *        Implicitly this will be one server object serving all clients
 55  *        It is not very smart
 56  *    </li>
 57  *    <li>Hack rmic to automatically pass a client id</li>
 58  * </ul>
 59  * We will choose the second solution (pass the sessionId).
 60  * <p />
 61  * RMI has not fine controlled policy like the CORBA POA!
 62  * <p />
 63  * A rmi-registry server is created automatically, if there is running already one, that is used.<br />
 64  * You can specify another port or host to create/use a rmi-registry server:
 65  * <pre>
 66  *     -plugin/rmi/registryPort   Specify a port number where rmiregistry listens.
 67  *                         Default is port 1099, the port 0 switches this feature off.
 68  *     -plugin/rmi/hostname Specify a hostname where rmiregistry runs.
 69  *                         Default is the localhost.
 70  * </pre>
 71  * <p />
 72  * Invoke options: <br />
 73  * <pre>
 74  *   java -Djava.rmi.server.codebase=file:///${XMLBLASTER_HOME}/classes/  \
 75  *        -Djava.security.policy=${XMLBLASTER_HOME}/config/xmlBlaster.policy \
 76  *        -Djava.rmi.server.hostname=hostname.domainname
 77  *        MyApp -plugin/rmi/registryPort 2078
 78  * </pre>
 79  * Another option is to include the directory of xmlBlaster.policy into
 80  * your CLASSPATH.
 81  *
 82  * @see <a href="http://www.xmlBlaster.org/xmlBlaster/doc/requirements/protocol.rmi.html">The RMI requirement</a>
 83  * @see <a href="http://java.sun.com/products/jdk/1.2/docs/guide/rmi/faq.html" target="others">RMI FAQ</a>
 84  * @see <a href="http://archives.java.sun.com/archives/rmi-users.html" target="others">RMI USERS</a>
 85  */
 86 public class RmiDriver implements I_Driver
 87 {
 88    private String ME = "RmiDriver";
 89    private Global glob = null;
 90    private static Logger log = Logger.getLogger(RmiDriver.class.getName());
 91    /** XmlBlaster RMI registry listen port is 1099, to access for bootstrapping */
 92    public static final int DEFAULT_REGISTRY_PORT = 1099;
 93    /** The singleton handle for this xmlBlaster server */
 94    private I_Authenticate authenticate = null;
 95    /** The singleton handle for this xmlBlaster server */
 96    private I_XmlBlaster xmlBlasterImpl = null;
 97    /** The RMI implementation, which delegates to authenticate */
 98    private AuthServerImpl authRmiServer = null;
 99    /** The name for the RMI registry */
100    private String authBindName = null;
101    /** The RMI implementation, which delegates to xmlBlaster server */
102    private XmlBlasterImpl xmlBlasterRmiServer = null;
103    /** The name for the RMI registry */
104    private String xmlBlasterBindName = null;
105    private boolean isActive = false;
106    /** The protocol plugin configuration */
107    private AddressServer addressServer;
108 
109 
110    /** Get a human readable name of this driver */
111    public String getName() {
112       return ME;
113    }
114 
115    /**
116     * Access the xmlBlaster internal name of the protocol driver. 
117     * @return "RMI"
118     */
119    public String getProtocolId() {
120       return "RMI";
121    }
122 
123    /** Enforced by I_Plugin */
124    public String getType() {
125       return getProtocolId();
126    }
127 
128    /** Enforced by I_Plugin */
129    public String getVersion() {
130       return "1.0";
131    }
132 
133    /**
134     * This method is called by the PluginManager (enforced by I_Plugin). 
135     * @see org.xmlBlaster.util.plugin.I_Plugin#init(org.xmlBlaster.util.Global,org.xmlBlaster.util.plugin.PluginInfo)
136     */
137    public void init(org.xmlBlaster.util.Global glob, org.xmlBlaster.util.plugin.PluginInfo pluginInfo) 
138       throws XmlBlasterException {
139 
140       this.glob = glob;
141       this.ME = "RmiDriver" + this.glob.getLogPrefixDashed();
142 
143 
144       org.xmlBlaster.engine.ServerScope engineGlob = (org.xmlBlaster.engine.ServerScope)glob.getObjectEntry(Constants.OBJECT_ENTRY_ServerScope);
145       if (engineGlob == null)
146          throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME + ".init", "could not retreive the ServerNodeScope. Am I really on the server side ?");
147       try {
148          this.authenticate = engineGlob.getAuthenticate();
149          if (this.authenticate == null) {
150             throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME + ".init", "authenticate object is null");
151          }
152          I_XmlBlaster xmlBlasterImpl = this.authenticate.getXmlBlaster();
153          if (xmlBlasterImpl == null) {
154             throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME + ".init", "xmlBlasterImpl object is null");
155          }
156 
157          init(glob, new AddressServer(glob, getType(), glob.getId(), pluginInfo.getParameters()), this.authenticate, xmlBlasterImpl);
158          
159          activate();
160       }
161       catch (XmlBlasterException ex) {
162          throw ex;
163       }
164       catch (Throwable ex) {
165          throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME + ".init", "init. Could'nt initialize the driver.", ex);
166       }
167    }
168 
169    /**
170     * Get the address how to access this driver. 
171     * @return "rmi://www.mars.universe:1099/I_AuthServer"
172     */
173    public String getRawAddress() {
174       return authBindName;
175    }
176 
177    /**
178     * Start xmlBlaster RMI access.
179     * @param glob Global handle to access logging, property and commandline args
180     */
181    private synchronized void init(Global glob, AddressServer addressServer, I_Authenticate authenticate, I_XmlBlaster xmlBlasterImpl) throws XmlBlasterException
182    {
183       this.authenticate = authenticate;
184       this.xmlBlasterImpl = xmlBlasterImpl;
185       this.addressServer = addressServer;
186 
187       XmlBlasterSecurityManager.createSecurityManager(glob);
188 
189       // plugin/rmi/registryPort
190       int registryPort = addressServer.getEnv("registryPort", DEFAULT_REGISTRY_PORT).getValue(); // default xmlBlaster RMI publishing port is 1099
191       String hostname = addressServer.getEnv("hostname", glob.getLocalIP()).getValue();
192 
193       try {
194          if (registryPort > 0) {
195             // Start a 'rmiregistry' if desired
196             try {
197                java.rmi.registry.LocateRegistry.createRegistry(registryPort);
198                log.info("Started RMI registry on port " + registryPort);
199             } catch (java.rmi.server.ExportException e) {
200                // Try to bind to an already running registry:
201                try {
202                   java.rmi.registry.LocateRegistry.getRegistry(hostname, registryPort);
203                   log.info("Another rmiregistry is running on port " + DEFAULT_REGISTRY_PORT +
204                                " we will use this one. You could change the port with e.g. '-plugin/rmi/registryPort 1122' to run your own rmiregistry.");
205                }
206                catch (RemoteException e2) {
207                   String text = "Port " + DEFAULT_REGISTRY_PORT + " is already in use, but does not seem to be a rmiregistry. Please can change the port with e.g. -plugin/rmi/registryPortCB=1122 : " + e.toString();
208                   log.severe(text);
209                   throw new XmlBlasterException(ME, text);
210                }
211             }
212          }
213 
214          String prefix = "rmi://";
215          authBindName = prefix + hostname + ":" + registryPort + "/I_AuthServer";
216          authBindName = addressServer.getEnv("AuthServerUrl", authBindName).getValue();
217 
218          xmlBlasterBindName = prefix + hostname + ":" + registryPort + "/I_XmlBlaster";
219          xmlBlasterBindName = addressServer.getEnv("XmlBlasterUrl", xmlBlasterBindName).getValue();
220       } catch (Exception e) {
221          e.printStackTrace();
222          throw new XmlBlasterException("InitRmiFailed", "Could not initialize RMI registry: " + e.toString());
223       }
224 
225       if (log.isLoggable(Level.FINE)) log.fine("Initialized RMI server");
226    }
227 
228    /**
229     * Activate xmlBlaster access through this protocol.
230     */
231    public synchronized void activate() throws XmlBlasterException {
232       if (log.isLoggable(Level.FINER)) log.finer("Entering activate");
233       try {
234          authRmiServer = new AuthServerImpl(glob, this.addressServer, this.authenticate, xmlBlasterImpl);
235          xmlBlasterRmiServer = new XmlBlasterImpl(glob, this.addressServer, xmlBlasterImpl);
236       }
237       catch (RemoteException e) {
238          log.severe(e.toString());
239          throw new XmlBlasterException("RmiDriverFailed", e.toString());
240       }
241 
242       bindToRegistry();
243 
244       isActive = true;
245 
246       log.info("Started successfully RMI driver.");
247    }
248 
249    /**
250     * Deactivate xmlBlaster access (standby), no clients can connect. 
251     */
252    public synchronized void deActivate() throws XmlBlasterException {
253       if (log.isLoggable(Level.FINER)) log.finer("Entering deActivate");
254       try {
255          if (authBindName != null) {
256             Naming.unbind(authBindName);
257          }
258          // force shutdown, even if we still have calls in progress:
259          java.rmi.server.UnicastRemoteObject.unexportObject(authRmiServer, true);
260       } catch (Exception e) {
261          log.warning("Can't shutdown authentication server: " + e.toString());
262       }
263 
264       try {
265          if (xmlBlasterBindName != null) Naming.unbind(xmlBlasterBindName);
266          // force shutdown, even if we still have calls in progress:
267          java.rmi.server.UnicastRemoteObject.unexportObject(xmlBlasterRmiServer, true);
268       } catch (Exception e) {
269          log.warning("Can't shutdown xmlBlaster server: " + e.toString());
270       }
271 
272       isActive = false;
273 
274       log.info("RMI deactivated, no client access possible.");
275    }
276 
277    /**
278     *  Instructs RMI to shut down.
279     */
280    public void shutdown() throws XmlBlasterException {
281       if (log.isLoggable(Level.FINE)) log.fine("Shutting down RMI driver ...");
282 
283       if (isActive) {
284          try {
285             deActivate();
286          } catch (XmlBlasterException e) {
287             log.severe(e.toString());
288          }
289       }
290 
291       authBindName = null;
292       log.info("RMI driver stopped, naming entries released.");
293    }
294 
295 
296    /**
297     * Publish the RMI xmlBlaster server to rmi registry.
298     * <p />
299     * The bind name is typically "rmi://localhost:1099/xmlBlaster"
300     * @exception XmlBlasterException
301     *                    RMI registry error handling
302     */
303    private void bindToRegistry() throws XmlBlasterException
304    {
305       if (log.isLoggable(Level.FINER)) log.finer("bindToRegistry() ...");
306 
307       // Publish RMI based xmlBlaster server ...
308       try {
309          Naming.bind(authBindName, authRmiServer);
310          log.info("Bound authentication RMI server to registry with name '" + authBindName + "'");
311       } catch (AlreadyBoundException e) {
312          try {
313             Naming.rebind(authBindName, authRmiServer);
314             log.warning("Removed another entry while binding authentication RMI server to registry with name '" + authBindName + "'");
315          }
316          catch(Exception e2) {
317             if (log.isLoggable(Level.FINE)) log.fine("RMI registry of '" + authBindName + "' failed: " + e2.toString());
318             throw new XmlBlasterException(ME+".RmiRegistryFailed", "RMI registry of '" + authBindName + "' failed: " + e2.toString());
319          }
320       } catch (java.rmi.NoSuchObjectException e) { // 'rmi://noty:7904/I_AuthServer': authRmiServer -> no such object in table
321          if (log.isLoggable(Level.FINE)) log.fine("RMI registry of '" + authBindName + "' authRmiServer=" + authRmiServer + " failed: " + e.getMessage());
322          throw new XmlBlasterException(glob, ErrorCode.RESOURCE_CONFIGURATION, ME+".RmiRegistryFailed",
323                                        "RMI registry of '" + authBindName + "' failed, probably another server instance is running already (implementation to handle this is missing): " + e.toString());
324       } catch (Throwable e) {
325          if (log.isLoggable(Level.FINE)) log.fine("RMI registry of '" + authBindName + "' failed: " + e.getMessage());
326          throw new XmlBlasterException(glob, ErrorCode.RESOURCE_CONFIGURATION, ME+".RmiRegistryFailed",
327                                        "RMI registry of '" + authBindName + "' failed: ", e);
328       }
329 
330       try {
331          Naming.bind(xmlBlasterBindName, xmlBlasterRmiServer);
332          log.info("Bound xmlBlaster RMI server to registry with name '" + xmlBlasterBindName + "'");
333       } catch (AlreadyBoundException e) {
334          try {
335             Naming.rebind(xmlBlasterBindName, xmlBlasterRmiServer);
336             log.warning("Removed another entry while binding xmlBlaster RMI server to registry with name '" + xmlBlasterBindName + "'");
337          } catch (Exception e2) {
338             log.severe("RMI registry of '" + xmlBlasterBindName + "' failed: " + e.toString());
339             throw new XmlBlasterException(glob, ErrorCode.RESOURCE_CONFIGURATION, ME+".RmiRegistryFailed", "RMI registry of '" + xmlBlasterBindName + "' failed: " + e.toString());
340          }
341       } catch (Throwable e) {
342          log.severe("RMI registry of '" + xmlBlasterBindName + "' failed: " + e.toString());
343          throw new XmlBlasterException(glob, ErrorCode.RESOURCE_CONFIGURATION, ME+".RmiRegistryFailed", "RMI registry of '" + xmlBlasterBindName + "' failed", e);
344       }
345    }
346 
347 
348    /**
349     * Command line usage.
350     */
351    public String usage()
352    {
353       String text = "\n";
354       text += "RmiDriver options:\n";
355       text += "   -plugin/rmi/registryPort\n";
356       text += "                       Specify a port number where rmiregistry listens.\n";
357       text += "                       Default is port "+DEFAULT_REGISTRY_PORT+", the port 0 switches this feature off.\n";
358       text += "   -plugin/rmi/hostname\n";
359       text += "                       Specify a hostname where rmiregistry runs.\n";
360       text += "                       Default is the localhost.\n";
361       text += "\n";
362       return text;
363    }
364 }


syntax highlighted by Code2HTML, v. 0.9.1