/* * Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.jboss.com.sun.corba.se.impl.oa.poa; import java.util.Set; import org.jboss.com.sun.corba.se.impl.oa.NullServantImpl; import org.jboss.com.sun.corba.se.impl.oa.poa.ActiveObjectMap.Key; import org.jboss.com.sun.corba.se.impl.orbutil.ORBUtility; import org.jboss.com.sun.corba.se.spi.oa.NullServant; import org.jboss.com.sun.corba.se.spi.oa.OAInvocationInfo; import org.omg.CORBA.SystemException; import org.omg.PortableServer.ForwardRequest; import org.omg.PortableServer.Servant; import org.omg.PortableServer.ServantActivator; import org.omg.PortableServer.ServantManager; import org.omg.PortableServer.POAPackage.NoServant; import org.omg.PortableServer.POAPackage.ObjectNotActive; import org.omg.PortableServer.POAPackage.WrongPolicy; /** * Implementation of POARequesHandler that provides policy specific operations on the POA. */ public class POAPolicyMediatorImpl_R_USM extends POAPolicyMediatorBase_R { protected ServantActivator activator; POAPolicyMediatorImpl_R_USM(Policies policies, POAImpl poa) { // assert policies.retainServants() super(policies, poa); activator = null; if (!policies.useServantManager()) throw poa.invocationWrapper().policyMediatorBadPolicyInFactory(); } /* * This handles a rather subtle bug (4939892). The problem is that enter will wait on the entry if it is being * etherealized. When the deferred state transition completes, the entry is no longer in the AOM, and so we need to * get a new entry, otherwise activator.incarnate will be called twice, once for the old entry, and again when a new * entry is created. This fix also required extending the FSM StateEngine to allow actions to throw exceptions, and * adding a new state in the AOMEntry FSM to detect this condition. */ private AOMEntry enterEntry(ActiveObjectMap.Key key) { AOMEntry result = null; boolean failed; do { failed = false; result = activeObjectMap.get(key); try { result.enter(); } catch (Exception exc) { failed = true; } } while (failed); return result; } protected Object internalGetServant(byte[] id, String operation) throws ForwardRequest { if (poa.getDebug()) { ORBUtility.dprint(this, "Calling POAPolicyMediatorImpl_R_USM.internalGetServant " + "for poa " + poa + " operation=" + operation); } try { ActiveObjectMap.Key key = new ActiveObjectMap.Key(id); AOMEntry entry = enterEntry(key); Object servant = activeObjectMap.getServant(entry); if (servant != null) { if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: servant already activated"); } return servant; } if (activator == null) { if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: no servant activator in POA"); } entry.incarnateFailure(); throw poa.invocationWrapper().poaNoServantManager(); } // Drop the POA lock during the incarnate call and re-acquire it afterwards. The entry state machine // prevents more than one thread from executing the incarnate method at a time within the same POA. try { if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: upcall to incarnate"); } poa.unlock(); servant = activator.incarnate(id, poa); if (servant == null) servant = new NullServantImpl(poa.omgInvocationWrapper().nullServantReturned()); } catch (ForwardRequest freq) { if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: incarnate threw ForwardRequest"); } throw freq; } catch (SystemException exc) { if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: incarnate threw SystemException " + exc); } throw exc; } catch (Throwable exc) { if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: incarnate threw Throwable " + exc); } throw poa.invocationWrapper().poaServantActivatorLookupFailed(exc); } finally { poa.lock(); // servant == null means incarnate threw an exception, while servant instanceof NullServant means // incarnate returned a null servant. Either case is an incarnate failure to the entry state machine. if ((servant == null) || (servant instanceof NullServant)) { if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: incarnate failed"); } // XXX Does the AOM leak in this case? Yes, but the problem is hard to fix. There may be a number of // threads waiting for the state to change from INCARN to something else, which is VALID or INVALID, // depending on the incarnate result. The activeObjectMap.get() call above creates an // ActiveObjectMap.Entry if one does not already exist, and stores it in the keyToEntry map in the // AOM. entry.incarnateFailure(); } else { // here check for unique_id policy, and if the servant is already registered for a different ID, // then throw OBJ_ADAPTER exception, else activate it. Section 11.3.5.1 99-10-07.pdf if (isUnique) { // check if the servant already is associated with some id if (activeObjectMap.contains((Servant) servant)) { if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: servant already assigned to ID"); } entry.incarnateFailure(); throw poa.invocationWrapper().poaServantNotUnique(); } } if (poa.getDebug()) { ORBUtility.dprint(this, "internalGetServant: incarnate complete"); } entry.incarnateComplete(); activateServant(key, entry, (Servant) servant); } } return servant; } finally { if (poa.getDebug()) { ORBUtility.dprint(this, "Exiting POAPolicyMediatorImpl_R_USM.internalGetServant " + "for poa " + poa); } } } public void returnServant() { OAInvocationInfo info = orb.peekInvocationInfo(); byte[] id = info.id(); ActiveObjectMap.Key key = new ActiveObjectMap.Key(id); AOMEntry entry = activeObjectMap.get(key); entry.exit(); } public void etherealizeAll() { if (activator != null) { Set<Key> keySet = activeObjectMap.keySet(); // Copy the elements in the set to an array to avoid changes in the set due to concurrent modification ActiveObjectMap.Key[] keys = keySet.toArray(new ActiveObjectMap.Key[keySet.size()]); for (int ctr = 0; ctr < keySet.size(); ctr++) { ActiveObjectMap.Key key = keys[ctr]; AOMEntry entry = activeObjectMap.get(key); Servant servant = activeObjectMap.getServant(entry); if (servant != null) { boolean remainingActivations = activeObjectMap.hasMultipleIDs(entry); // Here we etherealize in the thread that called this method, rather than etherealizing in a new // thread as in the deactivate case. We still inform the entry state machine so that only one thread // at a time can call the etherealize method. entry.startEtherealize(null); try { poa.unlock(); try { activator.etherealize(key.id, poa, servant, true, remainingActivations); } catch (Exception exc) { // ignore all exceptions } } finally { poa.lock(); entry.etherealizeComplete(); } } } } } public ServantManager getServantManager() throws WrongPolicy { return activator; } public void setServantManager(ServantManager servantManager) throws WrongPolicy { if (activator != null) throw poa.invocationWrapper().servantManagerAlreadySet(); if (servantManager instanceof ServantActivator) activator = (ServantActivator) servantManager; else throw poa.invocationWrapper().servantManagerBadType(); } public Servant getDefaultServant() throws NoServant, WrongPolicy { throw new WrongPolicy(); } public void setDefaultServant(Servant servant) throws WrongPolicy { throw new WrongPolicy(); } class Etherealizer extends Thread { private POAPolicyMediatorImpl_R_USM mediator; private ActiveObjectMap.Key key; private AOMEntry entry; private Servant servant; private boolean debug; public Etherealizer(POAPolicyMediatorImpl_R_USM mediator, ActiveObjectMap.Key key, AOMEntry entry, Servant servant, boolean debug) { this.mediator = mediator; this.key = key; this.entry = entry; this.servant = servant; this.debug = debug; } public void run() { if (debug) { ORBUtility.dprint(this, "Calling Etherealizer.run on key " + key); } try { try { mediator.activator.etherealize(key.id, mediator.poa, servant, false, mediator.activeObjectMap.hasMultipleIDs(entry)); } catch (Exception exc) { // ignore all exceptions } try { mediator.poa.lock(); entry.etherealizeComplete(); mediator.activeObjectMap.remove(key); POAManagerImpl pm = (POAManagerImpl) mediator.poa.the_POAManager(); POAFactory factory = pm.getFactory(); factory.unregisterPOAForServant(mediator.poa, servant); } finally { mediator.poa.unlock(); } } finally { if (debug) { ORBUtility.dprint(this, "Exiting Etherealizer.run"); } } } } public void deactivateHelper(ActiveObjectMap.Key key, AOMEntry entry, Servant servant) throws ObjectNotActive, WrongPolicy { if (activator == null) throw poa.invocationWrapper().poaNoServantManager(); Etherealizer eth = new Etherealizer(this, key, entry, servant, poa.getDebug()); entry.startEtherealize(eth); } public Servant idToServant(byte[] id) throws WrongPolicy, ObjectNotActive { ActiveObjectMap.Key key = new ActiveObjectMap.Key(id); AOMEntry entry = activeObjectMap.get(key); Servant servant = activeObjectMap.getServant(entry); if (servant != null) return servant; else throw new ObjectNotActive(); } }