package org.jacorb.poa;
/*
* JacORB - a free Java ORB
*
* Copyright (C) 1997-2014 Gerald Brose / The JacORB Team.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.jacorb.poa.except.POAInternalError;
import org.jacorb.poa.util.ByteArrayKey;
import org.jacorb.poa.util.POAUtil;
import org.jacorb.poa.util.StringPair;
import org.omg.CORBA.OBJ_ADAPTER;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.ServantActivator;
import org.omg.PortableServer.POAPackage.ObjectAlreadyActive;
import org.omg.PortableServer.POAPackage.ObjectNotActive;
import org.omg.PortableServer.POAPackage.ServantAlreadyActive;
import org.slf4j.Logger;
/**
* This class maps object id's to servants and vice versa.
* A oid/servant pair can be added/removed using add(),remove().
* The data can be retrieved using getServant() or getObjectId().
*
* @author Reimo Tiedemann, FU Berlin
*/
public class AOM
{
private AOMListener aomListener;
private final boolean unique;
private final Logger logger;
// an ObjectID can appear only once, but an servant can have multiple ObjectId's
// if MULTIPLE_ID is set
private final Map objectMap = new HashMap(); // oid -> servant
// only meaningful if UNIQUE_ID is set
// only for performance improvements (brose: is that still true?)
private final Map servantMap; // servant -> oid
// for synchronisation of servant activator calls
private final HashSet etherealisationList = new HashSet();
private final HashSet incarnationList = new HashSet();
private final HashSet deactivationList = new HashSet();
private BlockingQueue removalQueue = new LinkedBlockingQueue();
/**
* AOMRemoval thread - accessed from POA to signal shutdown.
*/
AOMRemoval aomRemoval;
/**
* Counter for AOMRemoval threads
*/
private static int count = 0;
/**
* Marker object used to signal the end of the removalQueue.
*/
private static final Object END = new Object();
protected AOM (boolean _unique, Logger _logger)
{
unique = _unique;
logger = _logger;
aomRemoval = new AOMRemoval ();
if (unique)
{
servantMap = new HashMap();
}
else
{
servantMap = Collections.EMPTY_MAP;
}
aomRemoval.setDaemon (true);
aomRemoval.start ();
}
/**
* <code>add</code> is called by the POA when activating an object
* to add a Servant into the Active Object Map.
*
* @param oid a <code>byte[]</code>, the id to use.
* @param servant a <code>Servant</code>, the servant to store.
* @exception ObjectAlreadyActive if an error occurs
* @exception ServantAlreadyActive if an error occurs
*/
protected void add( byte[] oid, Servant servant )
throws ObjectAlreadyActive, ServantAlreadyActive
{
ByteArrayKey oidbak = new ByteArrayKey (oid);
add(oidbak, servant);
}
protected synchronized void add(ByteArrayKey oidbak, Servant servant) throws ObjectAlreadyActive, ServantAlreadyActive
{
final byte[] oid = oidbak.getBytes();
/* an inCarnation and activation with the same oid has
priority, a reactivation for the same oid blocks until
etherealization is complete */
while (incarnationList.contains(oidbak) ||
etherealisationList.contains(oidbak) ||
// This is to check whether we are currently deactivating an
// object with the same id - if so, wait for the deactivate
// thread to complete.
deactivationList.contains( oidbak ) ||
// This is to check whether we are attempting to reactivate
// a servant that is currently being deactivated with another ID.
(
servantMap.containsKey( servant ) &&
deactivationList.contains(servantMap.get( servant ))
))
{
try
{
wait();
}
catch (InterruptedException e)
{
}
}
if (objectMap.containsKey(oidbak))
{
throw new ObjectAlreadyActive();
}
if (unique && servantMap.containsKey(servant))
{
throw new ServantAlreadyActive();
}
/* this is the actual object activation: */
objectMap.put(oidbak, servant);
if ( unique )
{
servantMap.put(servant, oidbak);
}
if (logger.isInfoEnabled())
{
logger.info("oid: " + POAUtil.convert(oid) +
"object is activated");
}
// notify an aom listener
if (aomListener != null)
{
aomListener.objectActivated(oid, servant, objectMap.size());
}
}
protected synchronized void addAOMListener(AOMListener listener)
{
aomListener = EventMulticaster.add(aomListener, listener);
}
synchronized boolean isDeactivating(ByteArrayKey oid)
{
return deactivationList.contains (oid);
}
protected synchronized boolean contains(Servant servant)
{
return _contains(servant);
}
private boolean _contains(Servant servant)
{
if (unique)
{
return servantMap.containsKey(servant);
}
else
{
return objectMap.containsValue(servant);
}
}
protected synchronized StringPair[] deliverContent()
{
final StringPair[] result = new StringPair[objectMap.size()];
Iterator en = objectMap.keySet().iterator();
for ( int i = 0; i < result.length; i++ )
{
final ByteArrayKey oidbak = (ByteArrayKey) en.next();
result[i] = new StringPair
(
oidbak.toString(),
objectMap.get(oidbak).getClass().getName()
);
}
return result;
}
protected synchronized byte[] getObjectId(Servant servant)
{
if (!unique)
{
throw new POAInternalError("error: not UNIQUE_ID policy (getObjectId)");
}
ByteArrayKey oidbak = (ByteArrayKey)servantMap.get(servant);
if (oidbak != null)
{
return oidbak.getBytes();
}
return null;
}
protected Servant getServant(byte[] oid)
{
return getServant( new ByteArrayKey( oid ) );
}
protected synchronized Servant getServant(ByteArrayKey oid)
{
return (Servant) objectMap.get(oid);
}
protected synchronized Servant incarnate( ByteArrayKey oidbak,
ServantActivator servant_activator,
org.omg.PortableServer.POA poa )
throws org.omg.PortableServer.ForwardRequest
{
final byte[] oid = oidbak.getBytes();
Servant servant = null;
if (logger.isInfoEnabled())
{
logger.info( "oid: " + POAUtil.convert(oid) +
"incarnate");
}
/* all invocations of incarnate on the servant manager are serialized */
/* all invocations of etherealize on the servant manager are serialized */
/* invocations of incarnate and etherialize are mutually exclusive */
while (!incarnationList.isEmpty() || !etherealisationList.isEmpty())
{
try
{
wait();
}
catch (InterruptedException e) {
}
}
/* another thread was faster, the incarnation is unnecessary now */
if (objectMap.containsKey(oidbak))
{
return (Servant) objectMap.get(oidbak);
}
/* servant incarnation */
if (servant_activator == null)
{
// This might be thrown if they failed to set implname
throw new OBJ_ADAPTER("Servant Activator for " + POAUtil.convert(oid) + " was null.");
}
incarnationList.add(oidbak);
try
{
servant = servant_activator.incarnate(oid, poa);
}
finally
{
incarnationList.remove(oidbak);
notifyAll();
}
if (servant == null)
{
if (logger.isInfoEnabled())
{
logger.info("oid: " + POAUtil.convert(oid) +
"servant is not incarnated (incarnate returns null)");
}
}
else
{
if (unique && servantMap.containsKey(servant))
{
if (logger.isInfoEnabled())
{
logger.info("oid: " + POAUtil.convert(oid) +
"servant is not incarnated (unique_id policy is violated)");
}
servant = null;
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("oid: " + POAUtil.convert(oid) +
"servant is incarnated");
}
// notify an aom listener
if (aomListener != null)
{
aomListener.servantIncarnated(oid, servant);
}
/* object activation */
try
{
add(oidbak, servant);
}
catch (ObjectAlreadyActive e)
{
throw new POAInternalError("error: object already active (AOM.incarnate)");
}
catch (ServantAlreadyActive e)
{
throw new POAInternalError("error: servant already active (AOM.incarnate)");
}
}
}
((org.jacorb.poa.POA)poa).getORB().set_delegate(servant);
return servant;
}
synchronized void remove( ByteArrayKey oidbak,
RequestController requestController,
ServantActivator servantActivator,
POA poa,
boolean cleanupInProgress)
throws ObjectNotActive
{
if ( !objectMap.containsKey( oidbak ) ||
deactivationList.contains( oidbak ) )
{
throw new ObjectNotActive();
}
deactivationList.add(oidbak);
RemovalStruct rs = new RemovalStruct (oidbak,
requestController,
servantActivator,
poa,
cleanupInProgress);
try
{
removalQueue.put (rs);
}
catch (InterruptedException ie)
{
}
}
/**
* <code>_remove</code> is spawned by remove to allow deactivate_object
* to return immediately.
*
* @param oidbak a <code>byte[]</code>, the id to use.
* @param requestController a <code>RequestController</code> value
* @param servantActivator a <code>ServantActivator</code> value
* @param poa a <code>POA</code> value
* @param cleanupInProgress a <code>boolean</code> value
*/
private void _remove( ByteArrayKey oidbak,
RequestController requestController,
ServantActivator servantActivator,
POA poa,
boolean cleanupInProgress)
{
final byte[] oid = oidbak.getBytes();
if (!objectMap.containsKey(oidbak))
{
// should not happen but ...
deactivationList.remove(oidbak);
return;
}
// wait for request completion on this object (see freeObject below)
if ( requestController != null)
{
requestController.waitForObjectCompletion(oidbak);
}
try
{
actualRemove(oidbak, servantActivator, poa, cleanupInProgress, oid);
}
finally
{
if (requestController != null)
{
requestController.freeObject(oidbak);
}
}
}
private synchronized void actualRemove(ByteArrayKey oidbak, ServantActivator servantActivator, POA poa, boolean cleanupInProgress, final byte[] oid)
{
Servant servant;
if ((servant = (Servant)objectMap.get(oidbak)) == null)
{
deactivationList.remove(oidbak);
return;
}
/* object deactivation */
objectMap.remove(oidbak);
if (unique)
{
servantMap.remove(servant);
}
// Wait to remove the oid from the deactivationList here so that the
// object map can be cleared out first. This ensures we don't
// reactivate an object we're currently deactivating.
deactivationList.remove(oidbak);
if (logger.isInfoEnabled())
{
logger.info("oid: " + POAUtil.convert(oid) +
"object is deactivated");
}
// notify an aom listener
if (aomListener != null)
{
aomListener.objectDeactivated(oid, servant, objectMap.size());
}
if (servantActivator == null)
{
// Tell anyone waiting we're done now.
notifyAll();
return;
}
/* servant etherealization */
/* all invocations of incarnate on the servant manager are
serialized, all invocations of etherealize on the
servant manager are serialized, invocations of incarnate
and etherialize are mutually exclusive */
while (!incarnationList.isEmpty() || !etherealisationList.isEmpty())
{
try
{
wait();
}
catch (InterruptedException e)
{
}
}
etherealisationList.add(oidbak);
try
{
servantActivator.etherealize
(
oid,
poa,
servant,
cleanupInProgress,
_contains(servant)
);
if (logger.isInfoEnabled())
{
logger.info("oid: " + POAUtil.convert(oid) +
"servant is etherealized");
}
// notify an aom listener
if (aomListener != null)
{
aomListener.servantEtherialized(oid, servant);
}
}
catch (org.omg.CORBA.SystemException e)
{
if (logger.isWarnEnabled())
{
logger.info("oid: " + POAUtil.convert(oid) +
"exception occurred during servant etherialisation: " + e.getMessage()
);
}
}
finally
{
etherealisationList.remove(oidbak);
notifyAll();
}
}
protected synchronized void removeAll( ServantActivator servant_activator,
POA poa,
boolean cleanup_in_progress )
{
final Iterator i = new HashSet(objectMap.keySet()).iterator();
while (i.hasNext())
{
final ByteArrayKey oid = (ByteArrayKey) i.next();
_remove(oid, null, servant_activator, poa, cleanup_in_progress);
}
}
protected synchronized void removeAOMListener(AOMListener listener)
{
aomListener = EventMulticaster.remove(aomListener, listener);
}
protected synchronized int size()
{
return objectMap.size();
}
class AOMRemoval extends Thread
{
private boolean run = true;
public AOMRemoval ()
{
super ("AOMRemoval-" + (++count));
}
public void end()
{
run = false;
try
{
removalQueue.put (END);
}
catch (InterruptedException ex)
{
}
}
public void run()
{
while (run)
{
try
{
Object rso = removalQueue.take();
if (rso != END)
{
RemovalStruct rs = (RemovalStruct)rso;
_remove (rs.oidbak,
rs.requestController,
rs.servantActivator,
rs.poa,
rs.cleanupInProgress);
}
}
catch (InterruptedException ie)
{
}
}
}
}
class RemovalStruct
{
ByteArrayKey oidbak;
RequestController requestController;
ServantActivator servantActivator;
POA poa;
boolean cleanupInProgress;
public RemovalStruct (ByteArrayKey oidbak,
RequestController requestController,
ServantActivator servantActivator,
POA poa,
boolean cleanupInProgress)
{
this.oidbak = oidbak;
this.requestController = requestController;
this.servantActivator = servantActivator;
this.poa = poa;
this.cleanupInProgress = cleanupInProgress;
}
}
}