diff options
author | Jose <jose@zeroc.com> | 2009-10-02 02:23:52 +0200 |
---|---|---|
committer | Jose <jose@zeroc.com> | 2009-10-02 02:23:52 +0200 |
commit | 1d9f29e281770ecdad4a245271f2b828bd64a32f (patch) | |
tree | ac083f28b06a444e484c24f8fcf1b12a36202c84 /java/src | |
parent | Updated demo README (diff) | |
download | ice-1d9f29e281770ecdad4a245271f2b828bd64a32f.tar.bz2 ice-1d9f29e281770ecdad4a245271f2b828bd64a32f.tar.xz ice-1d9f29e281770ecdad4a245271f2b828bd64a32f.zip |
3772. Recovering from Glacier2 / Ice router session failure.
Diffstat (limited to 'java/src')
-rw-r--r-- | java/src/Glacier2/Application.java | 552 | ||||
-rw-r--r-- | java/src/Glacier2/SessionFactoryHelper.java | 355 | ||||
-rw-r--r-- | java/src/Glacier2/SessionHelper.java | 496 | ||||
-rw-r--r-- | java/src/Ice/Application.java | 29 | ||||
-rw-r--r-- | java/src/IceInternal/Util.java | 16 |
5 files changed, 1430 insertions, 18 deletions
diff --git a/java/src/Glacier2/Application.java b/java/src/Glacier2/Application.java new file mode 100644 index 00000000000..fd0812d9958 --- /dev/null +++ b/java/src/Glacier2/Application.java @@ -0,0 +1,552 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package Glacier2; + +/** + * An extension of Ice.Application that makes it easy to write + * Glacier2 applications. + * + * <p> Applications must create a derived class that implements the + * {@link #createSession} and {@link #runWithSession} methods.<p> + * + * The base class invokes {@link #createSession} to create a new + * Glacier2 session and then invokes {@link #runWithSession} in + * which the subclass performs its application logic. The base class + * automatically destroys the session when {@link #runWithSession} + * returns. + * + * If {@link #runWithSession} calls {@link #restart} or raises any of + * the exceptions Ice.ConnectionRefusedException, + * Ice.ConnectionLostException, Ice.UnknownLocalException, + * Ice.RequestFailedException, or Ice.TimeoutException, the base + * class destroys the current session and restarts the application + * with another call to {@link #createSession} followed by + * {@link #runWithSession}. + * + * The application can optionally override the {@link #sessionDestroyed} + * callback method if it needs to take action when connectivity with + * the Glacier2 router is lost. + * + * A program can contain only one instance of this class. + * + * @see Ice.Application + * @see Glacier2.Router + * @see Glacier2.Session + * @see Ice.Communicator + * @see Ice.Logger + * @see #runWithSession + **/ +public abstract class Application extends Ice.Application +{ + /** + * This exception is raised if the session should be restarted. + */ + public class RestartSessionException extends Exception + { + } + + /** + * Initializes an instance that calls {@link Communicator#shutdown} if + * a signal is received. + **/ + public + Application() + { + } + + /** + * Initializes an instance that handles signals according to the signal + * policy. + * + * @param signalPolicy Determines how to respond to signals. + * + * @see SignalPolicy + **/ + public + Application(Ice.SignalPolicy signalPolicy) + { + super(signalPolicy); + } + + + /** + * Called once the communicator has been initialized and the Glacier2 session + * has been established. A derived class must implement <code>runWithSession</code>, + * which is the application's starting method. + * + * @param args The argument vector for the application. <code>Application</code> + * scans the argument vector passed to <code>main</code> for options that are + * specific to the Ice run time and removes them; therefore, the vector passed + * to <code>run</code> is free from Ice-related options and contains only options + * and arguments that are application-specific. + * + * @return The <code>runWithSession</code> method should return zero for successful + * termination, and non-zero otherwise. <code>Application.main</code> returns the + * value returned by <code>runWithSession</code>. + **/ + public abstract int + runWithSession(String[] args) + throws RestartSessionException; + + /** + * Run should not be overridden for Glacier2.Application. Instead + * <code>runWithSession</code> should be used. + */ + final public int + run(String[] args) + { + // This shouldn't be called. + assert false; + return 0; + } + + /** + * Called to restart the application's Glacier2 session. This + * method never returns. + * + * @throws RestartSessionException This exception is always thrown. + **/ + public void + restart() + throws RestartSessionException + { + throw new RestartSessionException(); + } + + /** + * Creates a new Glacier2 session. A call to + * <code>createSession</code> always precedes a call to + * <code>runWithSession</code>. If <code>Ice.LocalException</code> + * is thrown from this method, the application is terminated. + + * @return The Glacier2 session. + **/ + abstract public Glacier2.SessionPrx + createSession(); + + /** + * Called when the base class detects that the session has been destroyed. + * A subclass can override this method to take action after the loss of + * connectivity with the Glacier2 router. + **/ + public void + sessionDestroyed() + { + } + + /** + * Returns the Glacier2 router proxy + * @return The router proxy. + **/ + public static Glacier2.RouterPrx + router() + { + return _router; + } + + /** + * Returns the Glacier2 session proxy + * @return The session proxy. + **/ + public static Glacier2.SessionPrx + session() + { + return _session; + } + + /** + * Returns the category to be used in the identities of all of the client's + * callback objects. Clients must use this category for the router to + * forward callback requests to the intended client. + * @return The category. + * @throws SessionNotExistException No session exists. + **/ + public String + categoryForClient() throws SessionNotExistException + { + if(_router == null) + { + throw new SessionNotExistException(); + } + return _router.getCategoryForClient(); + } + + /** + * Create a new Ice identity for callback objects with the given + * identity name field. + * @return The identity. + * @throws SessionNotExistException No session exists. + **/ + public Ice.Identity + createCallbackIdentity(String name) throws SessionNotExistException + { + return new Ice.Identity(name, categoryForClient()); + } + + /** + * Adds a servant to the callback object adapter's Active Servant Map with a UUID. + * @param servant The servant to add. + * @return The proxy for the servant. + * @throws SessionNotExistException No session exists. + **/ + public Ice.ObjectPrx + addWithUUID(Ice.Object servant) throws SessionNotExistException + { + return objectAdapter().add(servant, createCallbackIdentity(java.util.UUID.randomUUID().toString())); + } + + /** + * Creates an object adapter for callback objects. + * @return The object adapter. + * @throws SessionNotExistException No session exists. + */ + public synchronized Ice.ObjectAdapter + objectAdapter() throws SessionNotExistException + { + if(_adapter == null) + { + if(_router == null) + { + throw new SessionNotExistException(); + } + // TODO: Depending on the resolution of + // http://bugzilla/bugzilla/show_bug.cgi?id=4264 the OA + // name could be an empty string. + String uuid = java.util.UUID.randomUUID().toString(); + _adapter = communicator().createObjectAdapterWithRouter(uuid, _router); + _adapter.activate(); + } + return _adapter; + } + + private class SessionPingThread extends Thread + { + SessionPingThread(Glacier2.RouterPrx router, long period) + { + _router = router; + _period = period; + _done = false; + } + + synchronized public void + run() + { + while(true) + { + _router.refreshSession_async(new Glacier2.AMI_Router_refreshSession() + { + public void + ice_response() + { + } + + public void + ice_exception(Ice.LocalException ex) + { + // Here the session has gone. The thread + // terminates, and we notify the + // application that the session has been + // destroyed. + done(); + sessionDestroyed(); + } + + public void + ice_exception(Ice.UserException ex) + { + // Here the session has gone. The thread + // terminates, and we notify the + // application that the session has been + // destroyed. + done(); + sessionDestroyed(); + } + }); + + if(!_done) + { + try + { + wait(_period); + } + catch(InterruptedException ex) + { + } + } + + if(_done) + { + break; + } + } + } + + public synchronized void + done() + { + if(!_done) + { + _done = true; + notify(); + } + } + + private final Glacier2.RouterPrx _router; + private final long _period; + private boolean _done = false; + } + + protected int + doMain(Ice.StringSeqHolder argHolder, Ice.InitializationData initData) + { + // Set the default properties for all Glacier2 applications. + initData.properties.setProperty("Ice.ACM.Client", "0"); + initData.properties.setProperty("Ice.RetryIntervals", "-1"); + + boolean restart; + Ice.IntHolder ret = new Ice.IntHolder(); + do + { + // A copy of the initialization data and the string seq + // needs to be passed to doMainInternal, as these can be + // changed by the application. + Ice.InitializationData id = (Ice.InitializationData)initData.clone(); + id.properties = id.properties._clone(); + Ice.StringSeqHolder h = new Ice.StringSeqHolder(); + h.value = argHolder.value.clone(); + + restart = doMain(h, id, ret); + } + while(restart); + return ret.value; + } + + private boolean + doMain(Ice.StringSeqHolder argHolder, Ice.InitializationData initData, Ice.IntHolder status) + { + // Reset internal state variables from Ice.Application. The + // remainder are reset at the end of this method. + _callbackInProgress = false; + _destroyed = false; + _interrupted = false; + + boolean restart = false; + status.value = 0; + + SessionPingThread ping = null; + try + { + _communicator = Ice.Util.initialize(argHolder, initData); + + _router = Glacier2.RouterPrxHelper.uncheckedCast(communicator().getDefaultRouter()); + if(_router == null) + { + Ice.Util.getProcessLogger().error("no glacier2 router configured"); + status.value = 1; + } + else + { + // + // The default is to destroy when a signal is received. + // + if(_signalPolicy == Ice.SignalPolicy.HandleSignals) + { + destroyOnInterrupt(); + } + + // If createSession throws, we're done. + try + { + _session = createSession(); + _createdSession = true; + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + status.value = 1; + } + + if(_createdSession) + { + ping = new SessionPingThread(_router, (_router.getSessionTimeout() * 1000) / 2); + ping.start(); + status.value = runWithSession(argHolder.value); + } + } + } + // We want to restart on those exceptions which indicate a + // break down in communications, but not those exceptions that + // indicate a programming logic error (ie: marshal, protocol + // failure, etc). + catch(RestartSessionException ex) + { + restart = true; + } + catch(Ice.ConnectionRefusedException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.ConnectionLostException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.UnknownLocalException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.RequestFailedException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.TimeoutException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + status.value = 1; + } + catch(java.lang.Exception ex) + { + Ice.Util.getProcessLogger().error("unknown exception" + IceInternal.Ex.toString(ex)); + status.value = 1; + } + catch(java.lang.Error err) + { + // + // We catch Error to avoid hangs in some non-fatal situations + // + Ice.Util.getProcessLogger().error("Java error " + IceInternal.Ex.toString(err)); + status.value = 1; + } + + // This clears any set interrupt. + if(_signalPolicy == Ice.SignalPolicy.HandleSignals) + { + defaultInterrupt(); + } + + synchronized(_mutex) + { + while(_callbackInProgress) + { + try + { + _mutex.wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + + if(_destroyed) + { + _communicator = null; + } + else + { + _destroyed = true; + // + // And _communicator != null, meaning will be + // destroyed next, _destroyed = true also ensures that + // any remaining callback won't do anything + // + } + } + + if(ping != null) + { + ping.done(); + while(true) + { + try + { + ping.join(); + break; + } + catch(InterruptedException ex) + { + } + } + ping = null; + } + + if(_createdSession && _router != null) + { + try + { + _router.destroySession(); + } + catch(Ice.ConnectionLostException ex) + { + // Expected: the router closed the connection. + } + catch(Glacier2.SessionNotExistException ex) + { + // This can also occur. + } + catch(Throwable ex) + { + // Not expected. + Ice.Util.getProcessLogger().error("unexpected exception when destroying the session:\n" + + IceInternal.Ex.toString(ex)); + } + _router = null; + } + + if(_communicator != null) + { + try + { + _communicator.destroy(); + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + status.value = 1; + } + catch(java.lang.Exception ex) + { + Ice.Util.getProcessLogger().error("unknown exception" + IceInternal.Ex.toString(ex)); + status.value = 1; + } + _communicator = null; + } + + synchronized(_mutex) + { + if(_appHook != null) + { + _appHook.done(); + } + } + + // Reset internal state. We cannot reset the Application state + // here, since _destroyed must remain true until we re-run + // this method. + _adapter = null; + _router = null; + _session = null; + _createdSession = false; + + return restart; + } + + private static Ice.ObjectAdapter _adapter; + private static Glacier2.RouterPrx _router; + private static Glacier2.SessionPrx _session; + private static boolean _createdSession = false; +} diff --git a/java/src/Glacier2/SessionFactoryHelper.java b/java/src/Glacier2/SessionFactoryHelper.java new file mode 100644 index 00000000000..ac77170b677 --- /dev/null +++ b/java/src/Glacier2/SessionFactoryHelper.java @@ -0,0 +1,355 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package Glacier2; + +/** + * A helper class for using Glacier2 with GUI applications. + * + * Applications should create a session factory for each Glacier2 router to which the application will + * connect. To connect with the Glacier2 router, call {@link SessionFactory#connect}. The callback object is + * notified of the various life cycle events. Once the session is torn down for whatever reason, the application + * can use the session factory to create another connection. + */ +public class SessionFactoryHelper +{ + /** + * A callback class to get notifications of status changes in the Glacier2 session. + * All callbacks on the <code>Callback</code> interface occur in the main swing thread. + */ + public interface Callback + { + /** + * Notifies the application that the communicator was created. + * + * @param session The Glacier2 session. + */ + void + createdCommunicator(SessionHelper session); + + /** + * Notifies the application that the Glacier2 session has been established. + * + * @param session The established session. + */ + + void + connected(SessionHelper session) + throws SessionNotExistException; + + /** + * Notifies the application that the Glacier2 session has been disconnected. + * + * @param session The disconnected session. + */ + void + disconnected(SessionHelper session); + + /** + * Notifies the application that the Glacier2 session establishment failed. + * + * @param session The session reporting the connection + * failure. + * @param ex The exception. + */ + void + connectFailed(SessionHelper session, Throwable ex); + } + + /** + * Creates a SessionFactory object. + * + * @param callback The callback object for notifications. + * @throws {@link Ice.InitializationException} + */ + public + SessionFactoryHelper(Callback callback) throws Ice.InitializationException + { + initialize(callback, new Ice.InitializationData(), Ice.Util.createProperties()); + } + + /** + * Creates a SessionFactory object. + * + * @param initData The initialization data to use when creating the communicator. + * @param callback The callback object for notifications. + * @throws {@link Ice.InitializationException} + */ + public + SessionFactoryHelper(Ice.InitializationData initData, Callback callback) throws Ice.InitializationException + { + initialize(callback, initData, initData.properties); + } + + /** + * Creates a SessionFactory object. + * + * @param properties The properties to use when creating the communicator. + * @param callback The callback object for notifications. + * @throws {@link Ice.InitializationException} + */ + public + SessionFactoryHelper(Ice.Properties properties, Callback callback) throws Ice.InitializationException + { + initialize(callback, new Ice.InitializationData(), properties); + } + + private void + initialize(Callback callback, Ice.InitializationData initData, Ice.Properties properties) + throws Ice.InitializationException + { + if(callback == null) + { + throw new Ice.InitializationException("Attempt to create a SessionFactoryHelper with a null Callback" + + "argument"); + } + + if(initData == null) + { + throw new Ice.InitializationException("Attempt to create a SessionFactoryHelper with a null " + + "InitializationData argument"); + } + + if(properties == null) + { + throw new Ice.InitializationException("Attempt to create a SessionFactoryHelper with a null Properties " + + "argument"); + } + + _callback = callback; + _initData = initData; + _initData.properties = properties; + + // + // Set default properties; + // + _initData.properties.setProperty("Ice.ACM.Client", "0"); + _initData.properties.setProperty("Ice.RetryIntervals", "-1"); + } + + /** + * Set the router object identity. + * + * @return The Glacier2 router's identity. + */ + synchronized public void + setRouterIdentity(Ice.Identity identity) + { + _identity = identity; + } + + /** + * Returns the object identity of the Glacier2 router. + * + * @return The Glacier2 router's identity. + */ + synchronized public Ice.Identity + getRouterIdentity() + { + return _identity; + } + + /** + * Sets the host on which the Glacier2 router runs. + * + * @param hostname The host name (or IP address) of the router host. + */ + synchronized public void + setRouterHost(String hostname) + { + _routerHost = hostname; + } + + /** + * Returns the host on which the Glacier2 router runs. + * + * @return The Glacier2 router host. + */ + synchronized public String + getRouterHost() + { + return _routerHost; + } + + /** + * Sets whether to connect with the Glacier2 router securely. + * + * @param secure If <code>true</code>, the client connects to the router + * via SSL; otherwise, the client connects via TCP. + */ + synchronized public void + setSecure(boolean secure) + { + _secure = secure; + } + + /** + * Returns whether the session factory will establish a secure connection to the Glacier2 router. + * + * @return The secure flag. + */ + synchronized public boolean + getSecure() + { + return _secure; + } + + /** + * Sets the connect and connection timeout for the Glacier2 router. + * + * @param timeoutMillisecs The timeout in milliseconds. A zero + * or negative timeout value indicates that the router proxy has no associated timeout. + */ + synchronized public void + setTimeout(int timeoutMillisecs) + { + _timeout = timeoutMillisecs; + } + + /** + * Returns the connect and connection timeout associated with the Glacier2 router. + * + * @return The timeout. + */ + synchronized public int + getTimeout() + { + return _timeout; + } + + /** + * Sets the Glacier2 router port to connect to. + * + * @param port The port. If 0, then the default port (4063 for TCP or 4064 for SSL) is used. + */ + synchronized public void + setPort(int port) + { + _port = port; + } + + /** + * Returns the Glacier2 router port to connect to. + * + * @return The port. + */ + synchronized public int + getPort() + { + return _port == 0 ? (_secure ? GLACIER2_TCP_PORT : GLACIER2_SSL_PORT) : _port; + } + + /** + * Returns the initialization data used to initialize the communicator. + * + * @return The initialization data. + */ + synchronized public Ice.InitializationData + getInitializationData() + { + return _initData; + } + + /** + * Connects to the Glacier2 router using the associated SSL credentials. + * + * Once the connection is established, {@link Callback#connected} is called on the callback object; + * upon failure, {@link Callback#connectFailed} is called with the exception. + * + * @return The connected session. + */ + synchronized public SessionHelper + connect() + { + SessionHelper session = new SessionHelper(_callback, createInitData()); + session.connect(); + return session; + } + + /** + * Connect the Glacier2 session using user name and password credentials. + * + * Once the connection is established, {@link Callback#connected} is called on the callback object; + * upon failure, {@link Callback#connectFailed) is called with the exception. + * + * @param username The user name. + * @param password The password. + * @return The connected session. + */ + synchronized public SessionHelper + connect(final String username, final String password) + { + SessionHelper session = new SessionHelper(_callback, createInitData()); + session.connect(username, password); + return session; + } + + private Ice.InitializationData + createInitData() + { + // Clone the initialization data and properties. + Ice.InitializationData initData = (Ice.InitializationData)_initData.clone(); + initData.properties = initData.properties._clone(); + + if(initData.properties.getProperty("Ice.Default.Router").length() == 0) + { + StringBuffer sb = new StringBuffer(); + sb.append("\""); + sb.append(Ice.Util.identityToString(_identity)); + sb.append("\""); + sb.append(":"); + + if(_secure) + { + sb.append("ssl -p "); + } + else + { + sb.append("tcp -p "); + } + + if(_port != 0) + { + sb.append(_port); + } + else + { + if(_secure) + { + sb.append(GLACIER2_SSL_PORT); + } + else + { + sb.append(GLACIER2_TCP_PORT); + } + } + + sb.append(" -h "); + sb.append(_routerHost); + if(_timeout > 0) + { + sb.append(" -t "); + sb.append(_timeout); + } + + initData.properties.setProperty("Ice.Default.Router", sb.toString()); + } + return initData; + } + + private Callback _callback; + private String _routerHost = "127.0.0.1"; + private Ice.InitializationData _initData; + private Ice.Identity _identity = new Ice.Identity("router", "Glacier2"); + private boolean _secure = true; + private int _port = 0; + private int _timeout = 10000; + private static final int GLACIER2_SSL_PORT = 4064; + private static final int GLACIER2_TCP_PORT = 4063; +} diff --git a/java/src/Glacier2/SessionHelper.java b/java/src/Glacier2/SessionHelper.java new file mode 100644 index 00000000000..bea4a5dec15 --- /dev/null +++ b/java/src/Glacier2/SessionHelper.java @@ -0,0 +1,496 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** +package Glacier2; + +import javax.swing.SwingUtilities; + +import Glacier2.SessionFactoryHelper.Callback; + +/** + * A helper class for using Glacier2 with GUI applications. + */ +public class SessionHelper +{ + private class SessionRefreshThread extends Thread + { + SessionRefreshThread(Glacier2.RouterPrx router, long period) + { + _router = router; + _period = period; + _done = false; + } + + synchronized public void + run() + { + while(true) + { + _router.refreshSession_async(new Glacier2.AMI_Router_refreshSession() + { + public void ice_response() + { + } + + public void ice_exception(Ice.LocalException ex) + { + done(); + SessionHelper.this.destroy(); + } + + public void ice_exception(Ice.UserException ex) + { + done(); + SessionHelper.this.destroy(); + } + }); + if(!_done) + { + try + { + wait(_period); + } + catch(InterruptedException ex) + { + } + } + if(_done) + { + break; + } + } + } + + public synchronized void + done() + { + if(!_done) + { + _done = true; + notify(); + } + } + + private final Glacier2.RouterPrx _router; + private final long _period; + private boolean _done = false; + } + + /** + * Creates a Glacier2 session. + * + * @param callback The callback for notifications about session establishment. + * @param initData The {@link Ice.InitializationData} for initializing the communicator. + */ + public SessionHelper(SessionFactoryHelper.Callback callback, Ice.InitializationData initData) + { + _callback = callback; + _initData = initData; + } + + /** + * Destroys the Glacier2 session. + * + * Once the session has been destroyed, {@link Callback.disconnected} is called on + * the associated callback object. + */ + synchronized public void + destroy() + { + if(_destroy) + { + return; + } + _destroy = true; + if(_refreshThread == null) + { + // In this case a connecting session is being + // destroyed. The communicator and session will be + // destroyed when the connection establishment has + // completed. + return; + } + _session = null; + + try + { + Runtime.getRuntime().removeShutdownHook(_shutdownHook); + } + catch(IllegalStateException ex) + { + // Ignore + } + catch(SecurityException ex) + { + // Ignore + } + + // Run the destroyInternal in a thread. This is because it + // destroyInternal makes remote invocations. + new Thread(new Runnable() + { + public void run() + { + destroyInternal(); + } + }).start(); + } + + /** + * Returns the session's communicator object. + * @return The communicator. + */ + synchronized public Ice.Communicator + communicator() + { + return _communicator; + } + + /** + * Returns the category to be used in the identities of all of the client's + * callback objects. Clients must use this category for the router to + * forward callback requests to the intended client. + * @return The category. + * @throws SessionNotExistException No session exists. + **/ + synchronized public String + categoryForClient() + throws SessionNotExistException + { + if(_router == null) + { + throw new SessionNotExistException(); + } + + return _router.getCategoryForClient(); + } + + /** + * Adds a servant to the callback object adapter's Active Servant Map with a UUID. + * @param servant The servant to add. + * @return The proxy for the servant. + * @throws SessionNotExistException No session exists. + **/ + public synchronized Ice.ObjectPrx + addWithUUID(Ice.Object servant) + throws SessionNotExistException + { + if(_router == null) + { + throw new SessionNotExistException(); + } + return internalObjectAdapter().add(servant, new Ice.Identity(java.util.UUID.randomUUID().toString(), + _router.getCategoryForClient())); + } + + /** + * Returns the Glacier2 session proxy. If the session hasn't been established yet, + * or the session has already been destroyed, throws SessionNotExistException. + * @return The session proxy, or throws SessionNotExistException if no session exists. + * @throws SessionNotExistException No session exists. + */ + synchronized public Glacier2.SessionPrx + session() + throws SessionNotExistException + { + if(_session == null) + { + throw new SessionNotExistException(); + } + + return _session; + } + + /** + * Returns true if there is an active session, otherwise returns false. + * @return <code>true</code>if session exists or false if no session exists. + */ + public boolean + isConnected() + { + synchronized(this) + { + return _connected; + } + } + + /** + * Creates an object adapter for callback objects. + * @return The object adapter. + * @throws SessionNotExistException No session exists. + */ + synchronized public Ice.ObjectAdapter + objectAdapter() + throws SessionNotExistException + { + return internalObjectAdapter(); + } + + // Only call this method when the calling thread owns the lock + private Ice.ObjectAdapter + internalObjectAdapter() + throws SessionNotExistException + { + if(_router == null) + { + throw new SessionNotExistException(); + } + if(_adapter == null) + { + // TODO: Depending on the resolution of + // http://bugzilla/bugzilla/show_bug.cgi?id=4264 the OA + // name could be an empty string. + _adapter = _communicator.createObjectAdapterWithRouter(java.util.UUID.randomUUID().toString(), _router); + _adapter.activate(); + } + return _adapter; + } + + private interface ConnectStrategy + { + Glacier2.SessionPrx + connect(Glacier2.RouterPrx router) + throws CannotCreateSessionException, PermissionDeniedException; + } + + /** + * Connects to the Glacier2 router using the associated SSL credentials. + * + * Once the connection is established, {@link Callback#connected} is called on the callback object; + * upon failure, {@link Callback#exception} is called with the exception. + */ + synchronized protected void + connect() + { + connectImpl(new ConnectStrategy() + { + public SessionPrx connect(RouterPrx router) + throws CannotCreateSessionException, PermissionDeniedException + { + return router.createSessionFromSecureConnection(); + } + }); + } + + /** + * Connects a Glacier2 session using user name and password credentials. + * + * Once the connection is established, {@link Callback#connected} is called on the callback object; + * upon failure {@link Callback.exception} is called with the exception. + * + * @param username The user name. + * @param password The password. + */ + synchronized protected void + connect(final String username, final String password) + { + connectImpl(new ConnectStrategy() + { + public SessionPrx connect(RouterPrx router) + throws CannotCreateSessionException, PermissionDeniedException + { + return router.createSession(username, password); + } + }); + } + + synchronized private void + connected(RouterPrx router, SessionPrx session) + { + _router = router; + + if(_destroy) + { + destroyInternal(); + return; + } + + // Assign the session after _destroy is checked. + _session = session; + _connected = true; + + assert _refreshThread == null; + _refreshThread = new SessionRefreshThread(_router, (_router.getSessionTimeout() * 1000)/2); + _refreshThread.start(); + + _shutdownHook = new Thread("Shutdown hook") + { + public void run() + { + SessionHelper.this.destroy(); + } + }; + + try + { + Runtime.getRuntime().addShutdownHook(_shutdownHook); + } + catch(IllegalStateException e) + { + // + // Shutdown in progress, ignored + // + } + catch(SecurityException ex) + { + // Ignore. Unsigned applets cannot registered shutdown + // hooks. + } + + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + try + { + _callback.connected(SessionHelper.this); + } + catch(SessionNotExistException ex) + { + SessionHelper.this.destroy(); + } + } + }); + } + + synchronized private void + destroyInternal() + { + assert _destroy; + + try + { + _router.destroySession(); + } + catch(Ice.ConnectionLostException e) + { + // Expected + } + catch(SessionNotExistException e) + { + // This can also occur. + } + catch(Throwable e) + { + // Not expected. + _communicator.getLogger().warning("SessionHelper: unexpected exception when destroying the session:\n" + e); + } + _router = null; + _connected = false; + + if(_refreshThread != null) + { + _refreshThread.done(); + while(true) + { + try + { + _refreshThread.join(); + break; + } + catch(InterruptedException e) + { + } + } + _refreshThread = null; + } + + try + { + _communicator.destroy(); + } + catch(Throwable ex) + { + } + _communicator = null; + + // Notify the callback that the session is gone. + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _callback.disconnected(SessionHelper.this); + } + }); + } + + private void + connectImpl(final ConnectStrategy factory) + { + assert !_destroy; + + try + { + _communicator = Ice.Util.initialize(_initData); + } + catch(final Ice.LocalException ex) + { + _destroy = true; + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _callback.connectFailed(SessionHelper.this, ex); + } + }); + return; + } + + new Thread(new Runnable() + { + public void run() + { + try + { + SwingUtilities.invokeAndWait(new Runnable() + { + public void run() + { + _callback.createdCommunicator(SessionHelper.this); + } + }); + + Glacier2.RouterPrx routerPrx = Glacier2.RouterPrxHelper.uncheckedCast( + _communicator.getDefaultRouter()); + Glacier2.SessionPrx session = factory.connect(routerPrx); + connected(routerPrx, session); + } + catch (final Exception ex) + { + try + { + _communicator.destroy(); + } + catch(Throwable ex1) + { + } + + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _callback.connectFailed(SessionHelper.this, ex); + } + }); + } + } + }).start(); + } + + private Ice.InitializationData _initData; + private Ice.Communicator _communicator; + private Ice.ObjectAdapter _adapter; + private Glacier2.RouterPrx _router; + private Glacier2.SessionPrx _session; + + private SessionRefreshThread _refreshThread; + private SessionFactoryHelper.Callback _callback; + private boolean _destroy = false; + private boolean _connected = false; + private Thread _shutdownHook; +} diff --git a/java/src/Ice/Application.java b/java/src/Ice/Application.java index 01b42196f8c..6bf0697681c 100644 --- a/java/src/Ice/Application.java +++ b/java/src/Ice/Application.java @@ -177,6 +177,12 @@ public abstract class Application Util.setProcessLogger(new LoggerI(initData.properties.getProperty("Ice.ProgramName"), "")); } + return doMain(argHolder, initData); + } + + protected int + doMain(StringSeqHolder argHolder, Ice.InitializationData initData) + { int status = 0; try @@ -427,7 +433,7 @@ public abstract class Application } /** - * Clears any shutdownn hooks, including any hook established with {@link #destroyOnInterrupt}code> or + * Clears any shutdown hooks, including any hook established with {@link #destroyOnInterrupt}code> or * {@link #shutdownOnInterrupt}. **/ public static void @@ -524,9 +530,10 @@ public abstract class Application } } - static class AppHook extends Thread + // For use by Glacier2.Application + static public class AppHook extends Thread { - void + public void done() { synchronized(_doneMutex) @@ -642,12 +649,12 @@ public abstract class Application private Thread _hook; } - private static String _appName; - private static Communicator _communicator; - private static AppHook _appHook; - private static java.lang.Object _mutex = new java.lang.Object(); - private static boolean _callbackInProgress = false; - private static boolean _destroyed = false; - private static boolean _interrupted = false; - private static SignalPolicy _signalPolicy = SignalPolicy.HandleSignals; + protected static String _appName; + protected static Communicator _communicator; + protected static AppHook _appHook; + protected static java.lang.Object _mutex = new java.lang.Object(); + protected static boolean _callbackInProgress = false; + protected static boolean _destroyed = false; + protected static boolean _interrupted = false; + protected static SignalPolicy _signalPolicy = SignalPolicy.HandleSignals; } diff --git a/java/src/IceInternal/Util.java b/java/src/IceInternal/Util.java index a9eebf126d2..17c53bfa3e6 100644 --- a/java/src/IceInternal/Util.java +++ b/java/src/IceInternal/Util.java @@ -138,14 +138,16 @@ public final class Util loadClass(String className, ClassLoader cl) throws LinkageError { - assert(cl != null); - try - { - return cl.loadClass(className); - } - catch(ClassNotFoundException ex) + if(cl != null) { - // Ignore + try + { + return cl.loadClass(className); + } + catch(ClassNotFoundException ex) + { + // Ignore + } } return null; |