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; } } }