// **********************************************************************
//
// Copyright (c) 2003-2010 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.
*/
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)
{
try
{
_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();
}
});
}
catch(Ice.CommunicatorDestroyedException ex)
{
//
// AMI requests can raise CommunicatorDestroyedException directly.
//
break;
}
if(!_done)
{
try
{
wait(_period);
}
catch(InterruptedException ex)
{
}
}
if(_done)
{
break;
}
}
}
synchronized public 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(SessionCallback callback, Ice.InitializationData initData)
{
_callback = callback;
_initData = initData;
}
/**
* Destroys the Glacier2 session.
*
* Once the session has been destroyed, {@link SessionCallback.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 _category;
}
/**
* 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.
**/
synchronized public 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(),
_category));
}
/**
* 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.
*/
synchronized public boolean
isConnected()
{
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)
{
_adapter = _communicator.createObjectAdapterWithRouter("", _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 SessionCallback#connected} is called on the callback object;
* upon failure, {@link SessionCallback#exception} is called with the exception.
*
* @param context The request context to use when creating the session.
*/
synchronized protected void
connect(final java.util.Map<String, String> context)
{
connectImpl(new ConnectStrategy()
{
public SessionPrx connect(RouterPrx router)
throws CannotCreateSessionException, PermissionDeniedException
{
return router.createSessionFromSecureConnection(context);
}
});
}
/**
* Connects a Glacier2 session using user name and password credentials.
*
* Once the connection is established, {@link SessionCallback#connected} is called on the callback object;
* upon failure {@link SessionCallback.exception} is called with the exception.
*
* @param username The user name.
* @param password The password.
* @param context The request context to use when creating the session.
*/
synchronized protected void
connect(final String username, final String password, final java.util.Map<String, String> context)
{
connectImpl(new ConnectStrategy()
{
public SessionPrx connect(RouterPrx router)
throws CannotCreateSessionException, PermissionDeniedException
{
return router.createSession(username, password, context);
}
});
}
synchronized private void
connected(RouterPrx router, SessionPrx session)
{
_router = router;
if(_destroy)
{
destroyInternal();
return;
}
//
// Cache the category.
//
_category = _router.getCategoryForClient();
//
// 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.
//
}
dispatchCallback(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 if another thread invoked on an object from the session concurrently.
//
}
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.
//
dispatchCallback(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;
dispatchCallback(new Runnable()
{
public void run()
{
_callback.connectFailed(SessionHelper.this, ex);
}
});
return;
}
new Thread(new Runnable()
{
public void run()
{
try
{
dispatchCallbackAndWait(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)
{
}
dispatchCallback(new Runnable()
{
public void run()
{
_callback.connectFailed(SessionHelper.this, ex);
}
});
}
}
}).start();
}
private void
dispatchCallback(Runnable runnable)
{
if(_initData.dispatcher != null)
{
_initData.dispatcher.dispatch(runnable, null);
}
else
{
runnable.run();
}
}
private void
dispatchCallbackAndWait(final Runnable runnable)
{
if(_initData.dispatcher != null)
{
final java.util.concurrent.Semaphore sem = new java.util.concurrent.Semaphore(0);
_initData.dispatcher.dispatch(
new Runnable()
{
public void
run()
{
runnable.run();
sem.release();
}
}, null);
sem.acquireUninterruptibly();
}
else
{
runnable.run();
}
}
private Ice.InitializationData _initData;
private Ice.Communicator _communicator;
private Ice.ObjectAdapter _adapter;
private Glacier2.RouterPrx _router;
private Glacier2.SessionPrx _session;
private String _category;
private SessionRefreshThread _refreshThread;
private SessionCallback _callback;
private boolean _destroy = false;
private boolean _connected = false;
private Thread _shutdownHook;
}