/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.container.corba;
import java.lang.reflect.Constructor;
import java.util.ConcurrentModificationException;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jacorb.orb.acs.AcsORBProfiler;
import org.jacorb.orb.acs.AcsProfilingORB;
import org.omg.CORBA.Any;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.CORBA.Policy;
import org.omg.CORBA.PolicyManager;
import org.omg.CORBA.PolicyManagerHelper;
import org.omg.CORBA.SetOverrideType;
import org.omg.Messaging.RELATIVE_RT_TIMEOUT_POLICY_TYPE;
import org.omg.PortableServer.IdAssignmentPolicyValue;
import org.omg.PortableServer.LifespanPolicyValue;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.omg.PortableServer.POAManager;
import org.omg.PortableServer.RequestProcessingPolicyValue;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.ServantRetentionPolicyValue;
import org.omg.PortableServer.POAManagerPackage.AdapterInactive;
import org.omg.PortableServer.POAManagerPackage.State;
import org.omg.PortableServer.POAPackage.AdapterAlreadyExists;
import org.omg.PortableServer.POAPackage.AdapterNonExistent;
import org.omg.PortableServer.POAPackage.InvalidPolicy;
import si.ijs.maci.ContainerHelper;
import alma.ACSErrTypeCommon.wrappers.AcsJUnexpectedExceptionEx;
import alma.JavaContainerError.wrappers.AcsJContainerEx;
import alma.JavaContainerError.wrappers.AcsJContainerServicesEx;
import alma.acs.concurrent.DaemonThreadFactory;
import alma.acs.container.AcsContainer;
import alma.acs.container.ComponentServantManager;
import alma.acs.logging.AcsLogger;
import alma.acs.util.StopWatch;
import alma.acs.util.UTCUtility;
/**
* This class contains methods to work with the ORB and POAs
* on behalf of {@link alma.acs.container.AcsContainer} and other classes
* from the <code>alma.acs.container</code> or <code>alma.acs.component.client</code> package.
* <p>
* An instance of <code>AcsCorba</code> encapsulates an ORB instance, together with the POAs.
* <p>
* POA structure:
* <ul>
* <li>RootPOA: does not directly host objects.
* <li>ContainerPOA: child of RootPOA, responsible for the container.
* <li>ComponentPOA: child of RootPOA, parent for the ComponentPOAxxx POAs,
* does not directly host objects.
* <li>ComponentPOAxxx: child of ComponentPOA; the POA for exactly one component
* (xxx stands for the component instance name). See {@link #createPOAForComponent(String)}.
* <li>offshootPoa: child of a ComponentPOAxxx (created on demand); it is the POA for any number
* of <code>OffShoot</code> objects for a given component. See {@link #getPOAForOffshoots(POA)}.
* </ul>
* <p>
* This class must not be used directly by ACS applications such as components or component clients.
* If you feel that you need to access ORB functionality, please request to have it provided
* in {@link alma.acs.container.ContainerServices} or {@link alma.acs.container.AdvancedContainerServices}.
* <p>
* created on Nov 4, 2002 2:56:44 PM
* @author hsommer
*/
public class AcsCorba
{
public static final String ORB_PROFILER_CLASS_PROPERTYNAME = "alma.acs.orb.profiler.class";
private org.omg.CORBA.ORB m_orb;
/**
* POA manager shared among RootPOA, ContainerPOA, component shared parent poa, offshoot poas, *BUT NOT with the component poas*.
*/
private POAManager sharedPoaManager;
private POA m_rootPOA;
private POA m_containerPOA;
private POA m_componentPOA;
private Policy[] m_compPolicies;
private Policy[] m_offshootPolicies;
// private boolean m_useRecoveryMode = false;
private AcsLogger m_logger;
private boolean m_isInitialized = false;
private Integer orbPort;
private Integer orbPortSearchRetry;
private boolean isOrbChoosingPort = false;
// copied from acscommandcenter. todo integrate better
static final public String SYSPROP_FIRESTARTER_OAPORT = "acs.firestarter.oaport";
protected int DEFAULT_PORT = 3075;
protected int DEFAULT_RETRY = 5;
/**
* ctor
*/
public AcsCorba(AcsLogger logger) {
m_logger = logger;
}
/**
*
* <p>
* Usage of parameters <code>orbPort</code> and <code>orbPortSearchRetry</code>:
* <pre>
* orbPort | orbPortSearchRetry
* (a) 1234 | null : try port 1234-1238
* (b) 1234 | 3 : try port 1234-1236
* (c) 1234 | 0 : use port 1234
* (d) 1234 | -1 : use port 1234
*
* (e) null | null : try port 3075-3079
* (f) null | 3 : try port 3075-3077
* (g) null | 0 : use port 3075
* (h) null | -1 : let orb choose port
* </pre>
* Notes: <ul>
* <li> 3075 can be overridden by system property SYSPROP_FIRESTARTER_OAPORT
* <li> (h) will fail miserably if an OAPort has been set in this virtual machine
* before
* </ul></p>
* The implementation of these port options has been moved here from acscommandcenter::Firestarter.
*
* @param orbPort use <code>null</code> for default
* @param orbPortSearchRetry use <code>null</code> for default
*/
public void setPortOptions(Integer orbPort, Integer orbPortSearchRetry) {
this.orbPort = orbPort;
this.orbPortSearchRetry = orbPortSearchRetry;
// catch the special case
if (this.orbPort == null && orbPortSearchRetry != null && -1 == orbPortSearchRetry.intValue()) {
this.isOrbChoosingPort = true;
return;
}
if (this.orbPort == null) {
this.orbPort = Integer.getInteger(SYSPROP_FIRESTARTER_OAPORT, DEFAULT_PORT);
}
if (this.orbPortSearchRetry == null) {
this.orbPortSearchRetry = new Integer(DEFAULT_RETRY);
}
}
public synchronized void setLogger(AcsLogger logger) {
m_logger = logger;
}
/**
* Initializes the ORB and POAs to be used by a Java container.
* <p>
* Calls {@link #setPortOptions(Integer, Integer) setPortOptions} to ensure that for container ORBs
* the specified port gets used. Thus any previous settings of port finding options will be overwritten.
*
* @param args command line arguments for the ORB
* @param port fixed port to be used by the ORB; this method will fail if the port is busy,
* since no retries with different ports will be done.
* @throws AcsJContainerServicesEx
*/
public synchronized void initCorba(String[] args, int port) throws AcsJContainerEx
{
if (isInitialized()) {
throw new IllegalStateException("Illegal call to initCorba. ORB/POAs have already been initialized.");
}
try {
setPortOptions(new Integer(port), new Integer(0));
prepareOrb(args);
// for a container we need the following POAs:
initPOAForContainer();
initPOAForComponents();
setInitialized(true);
} catch (Throwable thr) {
if (thr instanceof AcsJContainerEx) {
throw (AcsJContainerEx) thr;
}
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo("initCorba failed.");
throw ex;
}
// Our start script 'acsStartJavaContainer' sets the property alma.acs.orb.profiler.class,
// either using optional env var "JAVA_OPTIONS_ORB_PROFILER" or otherwise 'ContainerOrbProfiler' as default profiler.
initOrbProfiling();
}
/**
* Installs the ORB profiler if the profiler impl class is given in property {@value #ORB_PROFILER_CLASS_PROPERTYNAME}.
*/
private void initOrbProfiling() {
try {
String orbProfilerClassname = System.getProperty(ORB_PROFILER_CLASS_PROPERTYNAME);
if (orbProfilerClassname != null) {
if (m_orb instanceof AcsProfilingORB) {
Class<? extends AcsORBProfiler> orbProfilerClass = Class.forName(orbProfilerClassname).asSubclass(AcsORBProfiler.class);
Constructor<? extends AcsORBProfiler> ctor = null;
try {
ctor = orbProfilerClass.getConstructor(AcsLogger.class);
} catch (NoSuchMethodException ex) {
ctor = orbProfilerClass.getConstructor(Logger.class);
}
AcsORBProfiler profiler = ctor.newInstance(m_logger);
((AcsProfilingORB) m_orb).registerAcsORBProfiler(profiler);
m_logger.finer("Orb profiling set up, using " + orbProfilerClassname);
}
else {
m_logger.warning("Orb profiling was selected, but the currently used ORB " + m_orb.getClass().getName() + " does not support it.");
}
}
else {
m_logger.finer("Orb profiling was not selected (see property '" + ORB_PROFILER_CLASS_PROPERTYNAME + "').");
}
} catch (Throwable th) {
m_logger.log(Level.WARNING, "Failed to set up ORB profiling, will run without it.", th);
}
}
public synchronized boolean isInitialized() {
return m_isInitialized;
}
private synchronized void setInitialized(boolean initialized) {
m_isInitialized = initialized;
}
/**
* Initializes CORBA for the limited needs of a component client application.
* <p>
* If this class is used for client applications (outside a container),
* not all methods will be available (currently resulting in NPEs).
* Mainly <code>getORB()</code> is meant to be used.
*
* @param isAdmin if true, support for being an ACS administrator client will be created (TODO -- for Exec)
* @return the POA to be used by the client (happens to be the root POA)
* @throws Exception
* @see alma.acs.component.client.ComponentClient
*/
public synchronized POA initCorbaForClient(boolean isAdmin)
throws Exception
{
if (isInitialized()) {
throw new IllegalStateException("Illegal call to initCorba. ORB/POAs have already been initialized.");
}
try {
// todo: integrate better with #prepareORB
OrbConfigurator orbConf = OrbConfigurator.getOrbConfigurator();
setORB(ORB.init(orbConf.getOptions(), orbConf.getProperties()));
initRootPoa(m_orb);
sharedPoaManager.activate();
// @TODO when switching from JacORB to some other Java ORB in the future,
// it may be necessary to create a new thread here and call ORB.run,
// as the spec recommends doing this although JacORB does not require it:
// "Single threaded ORB implementations, and some multi-threaded ORB implementations,
// need the use of the main thread in order to function properly.
// For maximum portability, an application should call either run or perform_work on
// its main thread. run may be called by multiple threads simultaneously."
// In C++ ACS, the user is supposed to call SimpleClient.run,
// but for Java it seems better for ACS to handle this internally.
// Inside Java containers, this happens already via #blockOnORB
} catch (Exception ex) {
m_logger.log(Level.SEVERE, "failed to initialize CORBA.", ex);
if (m_orb != null) {
m_orb.destroy();
}
throw ex;
}
// Normally client apps do not use Corba profiling, but if they set property "alma.acs.orb.profiler.class"
// then we install the profiler (see http://ictjira.alma.cl/browse/ICT-4928).
initOrbProfiling();
setInitialized(true);
return m_rootPOA;
}
// /**
// * Initializes the <code>m_rootPOA</code> and <code>m_poaManager</code> fields
// * with the root POA and its POA manager.
// * @throws AcsJContainerServicesEx
// */
// private void initRootPoa() throws AcsJContainerServicesEx
// {
// m_rootPOA = null;
// try {
// m_rootPOA = POAHelper.narrow(m_orb.resolve_initial_references("RootPOA"));
//
// if (m_rootPOA != null) {
// m_logger.finest("RootPOA initialized.");
// }
// else {
// throw new AcsJContainerServicesEx("RootPOA == null");
// }
//
// // POA manager, same for all POAs
// m_poaManager = m_rootPOA.the_POAManager();
// if (m_poaManager == null) {
// throw new AcsJContainerServicesEx("POAManager == null");
// }
// }
// catch (AcsJContainerServicesEx ex) {
// throw ex;
// }
// catch (Exception ex) {
// throw new AcsJContainerServicesEx("Cannot resolve RootPOA: ", ex);
// }
//
// }
/**
* Initializes the root poa on the ORB provided as an argument.
* Also obtains the POA manager, but does not yet initialize it. See {@link #runCorba()}.
* <p>
* Note that this method does not work on <code>m_orb</code> member variable,
* since in method <code>trialAndError</code> we use tentative OBRs.
*
* @param orb
* @throws IllegalStateException if allocation failed, likely due to an unavailable port.
* TODO: use a better fitting exception instead
*/
protected void initRootPoa (ORB orb) {
m_rootPOA = null;
try {
m_logger.fine("calling orb.resolve_initial_references");
Object obj = orb.resolve_initial_references("RootPOA");
m_rootPOA = POAHelper.narrow(obj);
} catch (org.omg.CORBA.ORBPackage.InvalidName exc) {
throw new IllegalStateException("couldn't retrieve RootPOA: " + exc);
} catch (RuntimeException exc) {
throw new IllegalStateException("port taken?, couldn't retrieve RootPOA:" + exc);
}
try {
sharedPoaManager = m_rootPOA.the_POAManager();
} catch (Exception exc) {
throw new IllegalStateException("Failed to get POA manager: " + exc);
}
m_logger.fine("POA activated");
}
private void initPOAForContainer()
throws AcsJContainerEx
{
if (m_containerPOA != null) {
return;
}
Policy[] contPolicies = null;
try {
contPolicies = new Policy[4];
contPolicies[0] =
m_rootPOA.create_id_assignment_policy(IdAssignmentPolicyValue.USER_ID);
contPolicies[1] =
m_rootPOA.create_lifespan_policy(LifespanPolicyValue.PERSISTENT);
contPolicies[2] =
m_rootPOA.create_request_processing_policy(
RequestProcessingPolicyValue.USE_ACTIVE_OBJECT_MAP_ONLY);
contPolicies[3] =
m_rootPOA.create_servant_retention_policy(ServantRetentionPolicyValue.RETAIN);
m_containerPOA = m_rootPOA.create_POA("ContainerPOA", sharedPoaManager, contPolicies);
if (m_containerPOA == null) {
throw new NullPointerException("ContainerPOA reference == null");
}
m_logger.finest("ContainerPOA created.");
}
catch (Throwable thr) {
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo("ContainerPOA creation failed");
throw ex;
}
finally {
if (contPolicies != null) {
for (Policy policy : contPolicies) {
if (policy != null) {
policy.destroy();
}
}
}
}
}
/**
* Creates <code>m_componentPOA</code> as a child of the root POA.
* This POA will be the parent of the POAs for the individual components.
* Uses <code>LifespanPolicyValue.PERSISTENT</code>.
*
* @throws AcsJContainerServicesEx
*/
private void initPOAForComponents()
throws AcsJContainerEx
{
if (m_componentPOA != null) {
return;
}
try {
m_compPolicies = new Policy[4];
m_compPolicies[0] =
m_rootPOA.create_id_assignment_policy(IdAssignmentPolicyValue.USER_ID);
m_compPolicies[1] =
m_rootPOA.create_lifespan_policy(LifespanPolicyValue.PERSISTENT);
m_compPolicies[2] =
m_rootPOA.create_servant_retention_policy(ServantRetentionPolicyValue.RETAIN);
m_compPolicies[3] =
m_rootPOA.create_request_processing_policy(RequestProcessingPolicyValue.USE_SERVANT_MANAGER);
m_componentPOA = m_rootPOA.create_POA("ComponentPOA", sharedPoaManager, m_compPolicies);
if (m_componentPOA == null) {
throw new NullPointerException("ComponentPOA reference == null");
}
m_logger.finest("ComponentPOA created.");
}
catch (Throwable thr) {
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo("ComponentPOA creation failed");
throw ex;
}
}
/**
* Runs the ORB and POAs.
* Currently it calls <code>activate</code> on the poa manager.
* @throws AcsJContainerServicesEx
*/
public void runCorba() throws AcsJContainerEx
{
try {
sharedPoaManager.activate();
}
catch (Throwable thr) {
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo("running CORBA failed");
throw ex;
}
}
/**
* Calls {@link ORB#run()} which from the point of view of the caller blocks the thread until the ORB has shut down,
* optionally performing tasks for the ORB in the meantime (see Corba spec 2.4 , chap. 4.2.3.3)
*/
public void blockOnORB()
{
getORB().run();
}
/**
* Calls <code>m_orb.shutdown(wait_for_completion)</code>.
* <p>
* If this method is invoked from an ORB thread (as it happens in the Java container in case of shutdown being
* commanded by the manager), it returns immediately, independently of the <code>wait_for_completion</code>
* parameter. The <code>Orb#shutdown</code> call will then run asynchronously. Note that shutting down the ORB from
* within an ORB thread would result in a deadlock (or BAD_INV_ORDER exception) if <code>wait_for_completion</code>
* is true. The Java container will nonetheless wait for the ORB shutdown, because its main thread blocks on ORB#run
* and only continues when the ORB is shut down.
*
* @Todo: As a workaround for a JacORB bug (http://www.jacorb.org/cgi-bin/bugzilla/show_bug.cgi?id=537), we
* currently skim off the <code>ConcurrentModificationException</code> that the unsynchronized HashMap of
* <code>ClientConnectionManager.shutdown</code> may throw. This should be removed when we upgrade JacORB.
* @param wait_for_completion
* blocks this call until ORB has shut down. Will be effectively <code>false</code> if
* <code>isOrbThread == true</code>.
* @param isOrbThread
* must be <code>true</code> if the calling thread services a Corba invocation, e.g. to
* {@link AcsContainer#shutdown}.
* Note that JacORB has the proprietary method <code>isInInvocationContext</code>, but we need the explicit flag
* in order to stay ORB-independent.
*/
public void shutdownORB(final boolean wait_for_completion, boolean isOrbThread) {
if (m_orb != null) {
if (isOrbThread) {
Runnable cmd = new Runnable() {
public void run() {
try {
// since m_orb.shutdown runs asynchronously anyway, there is no advantage in passing
// wait_for_completion even if that flag is set to true.
m_orb.shutdown(false);
} catch (ConcurrentModificationException ex) {
// ignore, see javadoc
}
}
};
(new DaemonThreadFactory("ShutdownORB")).newThread(cmd).start();
} else {
// not an ORB thread, thus we can call shutdown directly
try {
if (wait_for_completion) {
// There appears to be a bug in JacORB that makes "orb.shutdown(true)" return too early (before shutdown completed),
// which then depending on timing can make a subsequent call to orb#destroy hang.
// Here we try out some additional synch'ing through Orb#run, and use a timeout to avoid blocking forever.
// However the premature return could be caused by the occasional ConcurrentModificationException (see comment on JacORB bug)
// in which case the additional orb shutdown synchronization on return from orb.run is useless.
// For ACS 8.0 we use both strategies though, the additional synch'ing and a sleep delay when catching ConcurrentModificationException.
final CountDownLatch synch1 = new CountDownLatch(1);
final CountDownLatch synch2 = new CountDownLatch(1);
Runnable cmd = new Runnable() {
public void run() {
synch1.countDown();
m_orb.run();
synch2.countDown();
}
};
// Start this ORB-blocking thread, and wait until it actually runs.
(new DaemonThreadFactory("WorkaroundBlockOnOrbTillShutdownCompletes")).newThread(cmd).start();
try {
synch1.await();
// sleep one second to minimize the risk that orb.run() has not made it to the point where it blocks
// when we call orb.shutdown next
Thread.sleep(1000);
} catch (InterruptedException ex1) {
// loggers probably don't work any more even though we have not called orb.shutdown yet,
// but the log manager and handlers has likely been flushed and shut down already
System.out.println("Failed to wait for the thread that should call orb.run to synch with ORB shutdown. " + ex1.toString());
}
// this call should only return when the ORB has shut down, but it may return too early
m_orb.shutdown(true);
// second line of defense, hoping that orb.run is not called prematurely even if
// orb.shutdown(true) returned too soon.
try {
boolean orbRunUnblocked = synch2.await(30, TimeUnit.SECONDS);
if (!orbRunUnblocked) {
// Loggers cannot be expected to work at this point, thus printing to stdout
System.out.println("Failed to return within 30 s from orb.run() after ORB shutdown.");
}
} catch (InterruptedException ex) {
// Loggers cannot be expected to work at this point, thus printing to stdout
System.out.println("InterruptedException waiting for orb.run() to return after ORB shutdown. " + ex.toString());
}
} else { // don't wait for orb shutdown to complete...
m_orb.shutdown(false);
}
} catch (ConcurrentModificationException ex) {
System.out.println("Caught a ConcurrentModificationException from ORB shutdown. "
+ "This should be harmless, see known JacORB bug http://www.jacorb.org/cgi-bin/bugzilla/show_bug.cgi?id=537");
if (wait_for_completion) {
// we try to compensate for leaving orb.shutdown too early by sleeping for a few seconds.
// It is not clear though if this will improve the chances that a subsequent orb.destroy will not hang.
// If it still hangs, we could try to sleep longer, or to throw a "shutdown failed" exception
// which the client could interpret to not call orb.destroy
try {
Thread.sleep(2000);
} catch (InterruptedException ex1) {
System.out.println("InterruptedException while sleeping after the ConcurrentModificationException in orb.shutdown");
}
}
}
}
}
}
/**
* Calls <code>m_orb.destroy()</code>.
* This method must not be invoked from an ORB invocation thread!
* <p>
* The Corba spec v. 2.4 says in section 4.2.3:
* <ul>
* <li> {@link ORB#shutdown(boolean)} causes all object adapters to be destroyed, since they cannot exist
* in the absence of an ORB. Shut down is complete when all ORB processing
* (including request processing and object deactivation or other operations associated with object adapters)
* has completed and the object adapters have been destroyed.
* In the case of the POA, this means that all object etherealizations have finished and root POA has been destroyed
* (implying that all descendent POAs have also been destroyed).
* <li> {@link ORB#destroy()} destroys the ORB so that its resources can be reclaimed by the application.
* <li> For maximum portability and to avoid resource leaks, an application should always call shutdown and destroy
* on all ORB instances before exiting.
* <li> Once an ORB has been destroyed, another call to ORB_init with the same ORBid will return a reference to a newly constructed ORB.
* <li> If destroy is called on an ORB that has not been shut down, it will start the shut down
* process and block until the ORB has shut down before it destroys the ORB.
* </ul>
* <p>
* With ACS 8.0, this method no longer attempts to destroy policy objects, trusting that either an explicit call
* to {@link AcsCorba#shutdownORB(boolean, boolean)} or the abovementioned implicit call to {@link ORB#shutdown(boolean)}
* takes care of this.
* <p>
* See also problems reported in COMP-2632.
*/
public void doneCorba() {
if (m_orb != null) {
// Loggers cannot be expected to work at this point, thus any printing would have to go to stdout.
// For backward compatibility we don't print these messages though, which before ACS 8.0 were dysfunctional logs.
//System.out.println("about to destroy the ORB");
try {
m_orb.destroy();
//System.out.println("ORB destroyed successfully.");
} catch (Exception ex) {
System.out.println("Exception occured during destruction of the ORB.\n" + ex.toString());
}
}
}
private void setORB(ORB orb) throws IllegalArgumentException {
if (orb == null) {
throw new IllegalStateException("ORB reference must not be null.");
}
this.m_orb = orb;
}
/**
* Returns the ORB instance.
* @return ORB
* @throws IllegalStateException if the ORB reference is missing or has not been initialized before.
*/
public ORB getORB()
{
if (m_orb == null) {
throw new IllegalStateException("ORB reference does not exist.");
}
if (!isInitialized()) {
throw new IllegalStateException("ORB has not been initialized.");
}
return m_orb;
}
/**
* @return the root POA used by the encapsulated ORB
* @throws IllegalStateException if the ORB has not been initialized before.
*/
public POA getRootPOA() {
getORB(); // to check init status
return m_rootPOA;
}
/**
* Activates the container using the respective POA, so that the container
* becomes a CORBA object.
*
* @param container the container servant
* @param name a name assigned to the container, used as the CORBA id.
* @return the container CORBA object, never null
* @throws AcsJContainerEx if args are null or the activation fails for whatever reason.
*/
public org.omg.CORBA.Object activateContainer(AcsContainer container, String name)
throws AcsJContainerEx
{
if (name == null || name.length() == 0 || container == null) {
String msg = "activateContainer called with missing parameter.";
AcsJContainerEx ex = new AcsJContainerEx();
ex.setContextInfo(msg);
throw ex;
}
m_logger.finer("entering activateContainer name=" + name);
org.omg.CORBA.Object actObj = null;
try {
byte[] id = name.getBytes();
// container is a CORBA Servant...
m_containerPOA.activate_object_with_id(id, container);
actObj = m_containerPOA.servant_to_reference(container);
actObj._hash(Integer.MAX_VALUE); // just to provoke an exc. if something is wrong with our new object
}
catch (Throwable thr) {
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo("failed to activate container object " + name);
throw ex;
}
return actObj;
}
/**
* Encapsulates {@link POA#servant_to_reference(Servant)} for the container servant.
* @since ACS 8.1 when {@link Servant#_this_object(ORB)} was replaced by {@link POA#servant_to_reference(Servant)}
* in {@link AcsContainer#loginToManager} to avoid a second activation of the container servant, on the RootPOA.
*/
public si.ijs.maci.Container getContainerCorbaRef(AcsContainer container)
throws AcsJContainerEx
{
try {
return ContainerHelper.narrow(m_containerPOA.servant_to_reference(container));
} catch (Exception ex) { // ServantNotActive, WrongPolicy (needs RETAIN and UNIQUE_ID or IMPLICIT_ACTIVATION)
AcsJContainerEx ex2 = new AcsJContainerEx(ex);
ex2.setContextInfo("failed to retrieve corba ref for container servant " + container.name());
throw ex2;
}
}
/**
* Creates a new POA that is responsible for exactly one component.
* Dependent CORBA objects (offshoots) are activated through a child POA.
* The new POA is created as a child of the shared ComponentPOA.
* <p>
* The new component POA uses its own POA manager, which allows discarding requests for this component
* (w/o discarding requests for other components, the container, or even this component's offshoots).
*
* @param compName the name of the component; used to construct the POA name by prepending "ComponentPOA_"
* @return the new component POA; never null.
* @throws AcsJContainerServicesEx if creation of the POA fails.
*/
public POA createPOAForComponent(String compName)
throws AcsJContainerEx
{
if (m_componentPOA == null) {
throw new IllegalStateException("Must call 'initPOAForComponents()' before 'createPOAForComponent'.");
}
POA compChildPOA = null;
String compChildPOAName = "ComponentPOA_" + compName;
try
{
try
{
// will create its own POAManager
compChildPOA = m_componentPOA.create_POA(compChildPOAName, null, m_compPolicies);
}
catch (org.omg.PortableServer.POAPackage.AdapterAlreadyExists ex1)
{
// todo: perhaps better create a new POA, since the old POA of the same name may belong
// to a shutting down component for which etherealization timed out.
// Perhaps mark a component POA as "useless" once manager calls deactivate on the container
m_logger.warning(compChildPOAName + " already exists even though it should not. Will try to reuse it...");
compChildPOA = m_componentPOA.find_POA(compChildPOAName, true);
}
compChildPOA.the_POAManager().activate();
}
catch (Throwable thr) {
AcsJContainerEx ex = new AcsJContainerEx();
ex.setContextInfo("failed to create POA for component " + compName);
throw ex;
}
return compChildPOA;
}
/**
* Creates a servant manager of type <code>ComponentServantManager</code>,
* attaches it to the POA, and activates the servant manager.
* <p>
* This <code>ComponentServantManager</code> can be used as a semaphore to synchronize
* with component etherealization during POA destruction.
* <p>
* Note that {@link org.omg.PortableServer.POAOperations#get_servant_manager()} will not return
* the <code>ComponentServantManager</code> implementation class, but instead a proxy object (_ServantActivatorStub).
* @param componentPOA a component POA
*
* @return the new <code>ComponentServantManager</code>.
* @throws AcsJContainerServicesEx
*/
public ComponentServantManager setServantManagerOnComponentPOA(POA componentPOA) throws AcsJContainerEx {
ComponentServantManager servantManager;
try {
servantManager = new ComponentServantManager(m_logger);
componentPOA.set_servant_manager(servantManager);
} catch (Throwable thr) {
String msg = "Failed to set a servant activator on the component POA " + componentPOA.the_name();
m_logger.log(Level.FINE, msg, thr);
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo(msg);
throw ex;
}
return servantManager;
}
/**
* Creates (or reuses) the non-persistent POA for offshoot objects as a child of the given POA.
* In spite of the suggestive name 'componentPOA', this POA can actually be any kind of POA,
* so that also client application can use the offshoot mechanism.
* <p>
* This method is synchronized inside to avoid race conditions between several offshoot POA creation/retrievals,
* where otherwise the POA would fail to be created even though first it was not found for reuse.
*
* @param componentPOA the POA of the component that owns the offshoot object;
* will become the parent of the offshoot POA.
* @return POA non-persistent POA for offshoot objects for the current component.
*
* @throws AcsJContainerServicesEx
*/
public POA getPOAForOffshoots(POA componentPOA)
throws AcsJContainerEx, AcsJUnexpectedExceptionEx
{
final String offshootPoaName = "offshootPoa";
POA offshootPoa = null;
synchronized (componentPOA) {
try {
// can we reuse it?
offshootPoa = componentPOA.find_POA(offshootPoaName, false);
}
catch (AdapterNonExistent e) {
m_logger.finest("will have to create offshoot POA");
if (m_offshootPolicies == null)
{
m_offshootPolicies = new Policy[4];
m_offshootPolicies[0] =
componentPOA.create_id_assignment_policy(IdAssignmentPolicyValue.SYSTEM_ID);
m_offshootPolicies[1] =
componentPOA.create_lifespan_policy(LifespanPolicyValue.TRANSIENT);
m_offshootPolicies[2] =
componentPOA.create_request_processing_policy(
RequestProcessingPolicyValue.USE_ACTIVE_OBJECT_MAP_ONLY);
m_offshootPolicies[3] =
componentPOA.create_servant_retention_policy(ServantRetentionPolicyValue.RETAIN);
}
try {
offshootPoa = componentPOA.create_POA(
offshootPoaName, sharedPoaManager, m_offshootPolicies);
m_logger.finest("successfully created offshoot POA");
}
catch (InvalidPolicy ex) {
AcsJContainerEx ex2 = new AcsJContainerEx(ex);
ex2.setContextInfo("Attempted to create offshoot POA with invalid policies.");
throw ex2;
}
catch (AdapterAlreadyExists ex) {
// we sync on componentPOA, so this should never happen
throw new AcsJUnexpectedExceptionEx(ex);
}
}
}
return offshootPoa;
}
/**
* Activates a component using a given component POA.
* @param servant
* @param name
* @param compPOA
* @return the component as a CORBA object
* @throws AcsJContainerServicesEx
*/
public org.omg.CORBA.Object activateComponent(Servant servant, String name, POA compPOA)
throws AcsJContainerEx
{
if (name == null || name.length() == 0 ||
servant == null || compPOA == null)
{
AcsJContainerEx ex = new AcsJContainerEx();
ex.setContextInfo("activateComponent called with missing parameter.");
throw ex;
}
m_logger.finer("entering activateComponent: name=" + name);
org.omg.CORBA.Object actObj = null;
try {
byte[] id = name.getBytes();
compPOA.activate_object_with_id(id, servant);
actObj = compPOA.servant_to_reference(servant);
actObj._hash(Integer.MAX_VALUE); // just to provoke an exc. if something is wrong with our new object
m_logger.finer("component '" + name + "' activated as CORBA object.");
}
catch (Throwable thr) {
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo("failed to activate component " + name);
throw ex;
}
return actObj;
}
/**
* Deactivates a component's POA manager.
* The effect is that no further calls will reach the component.
* This method returns immediately if the POA manager is already inactive.
* Otherwise it will only return when the active requests are done.
* <p>
* Note for JacORB (1.4 as well as 2.2.4): there seems to be a problem in
* <code>org.jacorb.poa.RequestController#waitForCompletion</code> with local calls (e.g. collocated component).
* Instead of duly waiting for completion, that method returns immediately when such calls are still executing.
* Even worse, <code>RequestController#activeRequestTable</code> seems to never get touched in case of local calls.
* There is a currently commented out JUnit test that verifies this problem.
* <p>
* The purpose of this method is to allow the container to "drain" a component of requests,
* so that <code>cleanUp</code> can be called while no functional calls are running or can come in.
* An alternative to using the component POA manager could be to destroy the component POA before
* calling <code>cleanUp</code>. This has the disadvantage of also destroying the offshoot child POA,
* which is needed to properly clean up callback connections.
* <p>
* Note that {@link POAManager#deactivate(boolean, boolean)} is called in a separate thread,
* so that this method itself may well be called from an ORB thread.
* This method uses <code>etherealize_objects=false</code> and <code>wait_for_completion=true</code>.
*
* @param compPOA the component POA
* @param compName component instance name
* @param timeoutMillis timeout in milliseconds after which this call returns even if the POA manager is not inactive yet.
* @return true if the POA manager is inactive.
*/
public boolean deactivateComponentPOAManager(POA compPOA, final String compName, int timeoutMillis) {
final POAManager compPOAManager = compPOA.the_POAManager();
if (compPOAManager.get_state() == State.INACTIVE) {
return true;
}
final CountDownLatch deactivateSyncer = new CountDownLatch(1);
// todo: use thread pool instead of always creating a thread for this purpose
Thread discardRequestsThread = new Thread(new Runnable() {
public void run() {
try {
// note that deactivate(wait_for_completion=true) must not be called from an ORB thread,
// thus we use a separate thread here. This is quite annoying because
// at least in JacORB's implementation, another new thread is created inside deactivate
compPOAManager.deactivate(false, true);
// compPOAManager.discard_requests(true);
deactivateSyncer.countDown();
} catch (AdapterInactive e) {
m_logger.log(Level.INFO, "Failed to finish and reject requests for component " + compName, e);
}
}
});
discardRequestsThread.setDaemon(true);
discardRequestsThread.setName("deactivatePOAManager_" + compName);
StopWatch stopWatch = null;
if (m_logger.isLoggable(Level.FINEST)) {
stopWatch = new StopWatch();
}
discardRequestsThread.start();
boolean isInactive = false;
try {
isInactive = deactivateSyncer.await(timeoutMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// isInactive == false
} finally {
if (m_logger.isLoggable(Level.FINEST)) {
long deactivationTime = stopWatch.getLapTimeMillis();
String msg = "POA manager deactivation for component '" + compName + "' was " + (isInactive ? "" : "*not* ") +
"successful and took " + deactivationTime + " ms. " +
"The component can" + (isInactive ? "not" : "") + " receive further calls over CORBA.";
m_logger.finest(msg);
}
}
return isInactive;
}
/**
* Destroys the component POA (and its child the offshoot POA if it exists),
* waiting at most <code>timeoutMillis</code> milliseconds for active requests to terminate.
*
* @param compPOA the component POA
* @param compServantManager the custom servant manager of that POA
* @param timeoutMillis the timeout in milliseconds after which the call returns even if the POA has not been destroyed.
* @return true if the component was etherealized within the given time, false otherwise
* @see ComponentServantManager#waitForEtherealize(int)
*/
public boolean destroyComponentPOA(POA compPOA, ComponentServantManager compServantManager, int timeoutMillis) {
compServantManager.resetWaitForEtherealize();
// wait_for_completion == false: we synchronize instead by using a special servant manager.
// Note that wait_for_completion must be false when calling from an ORB thread (otherwise BAD_INV_ORDER with standard minor code 3),
// thus by sync'ing with etherialization we avoid creating an additional thread which would be needed to trick the ORB-thread-check.
synchronized (compPOA) {
compPOA.destroy(true, false);
}
// sync with component etherealization.
// Note that a steady stream of calls to this component can effectively prevent deactivation,
// therefore we use a timeout.
return compServantManager.waitForEtherealize(timeoutMillis);
}
/**
* Activates an offshoot object (which is a regular CORBA object
* owned by a component).
* <p>
* All offshoot objects for a component are activated using a non-persistent "offshootPOA".
*
* @param servant the offshoot servant.
* @param compPOA the POA responsible for the component which activates the offshoot.
* @return the activated offshoot corba object.
* @throws AcsJContainerServicesEx
*/
public org.omg.CORBA.Object activateOffShoot(Servant servant, POA compPOA)
throws AcsJContainerEx, AcsJUnexpectedExceptionEx
{
if (servant == null || compPOA == null) {
String msg = "activateOffShoot called with missing parameter.";
AcsJContainerEx ex = new AcsJContainerEx();
ex.setContextInfo(msg);
throw ex;
}
POA offshootPoa = getPOAForOffshoots(compPOA);
org.omg.CORBA.Object actObj = null;
try
{
offshootPoa.activate_object(servant);
actObj = offshootPoa.servant_to_reference(servant);
actObj._hash(Integer.MAX_VALUE); // just to provoke an exc. if something is wrong with our new object
m_logger.finer("offshoot of type '" + servant.getClass().getName() +
"' activated as a CORBA object.");
}
catch (Throwable thr)
{
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo("failed to activate offshoot of type '" +
servant.getClass().getName());
throw ex;
}
return actObj;
}
public void deactivateOffShoot(Servant servant, POA compPOA) throws AcsJContainerEx {
if (servant == null || compPOA == null) {
String msg = "deactivateOffShoot called with missing parameter.";
AcsJContainerEx ex = new AcsJContainerEx();
ex.setContextInfo(msg);
throw ex;
}
byte[] id = null;
try {
POA offshootPoa = getPOAForOffshoots(compPOA);
id = offshootPoa.servant_to_id(servant);
offshootPoa.deactivate_object(id);
}
catch (AcsJContainerEx e) {
throw e;
}
catch (Throwable thr) {
String msg = "failed to deactivate offshoot of type '" + servant.getClass().getName() +
"' (ID=" + String.valueOf(id) + ")";
m_logger.log(Level.WARNING, msg, thr);
AcsJContainerEx ex = new AcsJContainerEx(thr);
ex.setContextInfo(msg);
throw ex;
}
}
/**
* Sets the roundtrip timeout for all calls going out through the ORB that is encapsulated by this class.
* For example, this applies to all corba calls made by a container and any of its components.
* @param timeoutSeconds
* @throws AcsJContainerServicesEx
* @see {@link #wrapForRoundtripTimeout(Object, double)}
*/
public void setORBLevelRoundtripTimeout(double timeoutSeconds) throws AcsJContainerServicesEx {
if (!isInitialized()) {
throw new IllegalStateException("Only call when this object has been initialized!");
}
try
{
Any rrtPolicyAny = m_orb.create_any();
rrtPolicyAny.insert_ulonglong(UTCUtility.durationJavaMillisToOmg((long)timeoutSeconds*1000));
Policy p = m_orb.create_policy(RELATIVE_RT_TIMEOUT_POLICY_TYPE.value, rrtPolicyAny);
// about PolicyManager, see Corba spec (2.4) section 4.9.1
PolicyManager pm = PolicyManagerHelper.narrow(m_orb.resolve_initial_references("ORBPolicyManager"));
pm.set_policy_overrides(new Policy[]{ p }, SetOverrideType.SET_OVERRIDE);
p.destroy();
}
catch (Throwable thr) {
AcsJContainerServicesEx ex2 = new AcsJContainerServicesEx(thr);
ex2.setContextInfo("Failed to set the ORB-level client-side corba roundtrip timeout to " + timeoutSeconds);
throw ex2;
}
m_logger.fine("Set ORB level roundtrip timeout to " + timeoutSeconds + " seconds.");
}
/**
* <p>
* Impl note: The corba spec (v 2.4, 4.3.8.1) describes the difference between
* SetOverrideType.SET_OVERRIDE and SetOverrideType.ADD_OVERRIDE. It is not clear to me (HSO)
* which one should be used, or if there is no practical difference.
* @param corbaRef
* @param timeoutSeconds
* @return
* @throws AcsJContainerServicesEx
* @see {@link #setORBLevelRoundtripTimeout(double)}
*/
public org.omg.CORBA.Object wrapForRoundtripTimeout(org.omg.CORBA.Object corbaRef, double timeoutSeconds) throws AcsJContainerServicesEx {
if (!isInitialized()) {
throw new IllegalStateException("Only call when this object has been initialized!");
}
org.omg.CORBA.Object ret = null;
try
{
Any rrtPolicyAny = m_orb.create_any();
rrtPolicyAny.insert_ulonglong(UTCUtility.durationJavaMillisToOmg((long)timeoutSeconds*1000));
Policy p = m_orb.create_policy(RELATIVE_RT_TIMEOUT_POLICY_TYPE.value, rrtPolicyAny);
ret = corbaRef._set_policy_override (new Policy[]{ p }, SetOverrideType.SET_OVERRIDE);
p.destroy();
}
catch (Throwable thr) {
AcsJContainerServicesEx ex2 = new AcsJContainerServicesEx(thr);
ex2.setContextInfo("Failed to set the object-level client-side corba roundtrip timeout to " + timeoutSeconds);
throw ex2;
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////////////////////
////// code integrated from Exec::Firestarter.java
/////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets an ORB and initializes the root POA using {@link #initRootPoa(ORB)}.
* <p>
* If this method has been called before, the same ORB will be returned.
* Depending on the values given in {@link #setPortOptions(Integer, Integer)},
* a fixed port will be used (resulting in a failure if it can't be obtained),
* or the port will be chosen from a given range, or the ORB will be allowed to pick a port.
* <p>
* This class is <i>fail-fast</i> in that it tries to create an orb
* instantly on a call to this method. If this first call goes well, subsequent calls
* will succeed (aside from other general errors).
*
* @param args
* @throws AcsJContainerServicesEx
*/
public void prepareOrb(String[] args) {
if (m_orb == null) {
// deal with the special case
if (this.isOrbChoosingPort) {
ORB orb = createOrb(args, null);
initRootPoa(orb);
setORB(orb);
return;
}
if (this.orbPortSearchRetry.intValue() <= 0) {
ORB orb = createOrb(args, this.orbPort);
initRootPoa(orb);
setORB(orb);
} else {
RVtrialAndError re = trialAndError(args, orbPort.intValue(), orbPortSearchRetry.intValue());
setORB(re.orb);
}
}
}
/**
* Creates an ORB with a port determined by the values given in {@link #setPortOptions(Integer, Integer)}.
* <p>
* Aside from the logger, no instance variables are used in this method.
*
* @param args command line arguments for the ORB
* @param port If port == null, OAPort property will not be set.
* @return
*/
private ORB createOrb(String[] args, Integer port) {
ORB orb = null;
// sets CORBA options
OrbConfigurator orbConf = OrbConfigurator.getOrbConfigurator();
// IFR access is generally discouraged but needed if clients such as the sampling manager call "get_interface"
// on a reference to a corba object inside this container.
orbConf.setORBInitRef("InterfaceRepository", System.getProperty("ACS.repository"));
// orbConf.setORBInitRef("NameService", System.getProperty("ACS.???")); // currently the JacORB-specific property ORBInitRef.NameService is set instead (acsStartJava)
orbConf.setOptions(args);
if (port != null) {
orbConf.setPort(port.intValue());
}
String[] orbOpts = orbConf.getOptions();
/*
* We start an ORB, which needs its own port ('OAPort'). There are, however, no
* conventions in Acs which port to assign to our ORB (which will be used by a
* ComponentClient or a Supervisor client). There is, on the other hand, an
* automatism in an ORB that finds free ports.
*
* Thus: Don't set the OAPort property, instead let ORB choose the port itself
*
* Problem: jacorb won't choose a port by itself when an OAPort has ever been set in
* this VM before
*/
boolean suppressPortProperty = (port==null);
Properties orbProps = orbConf.getProperties(suppressPortProperty);
StringBuffer logBuf = new StringBuffer("ORB options ");
for (int i = 0; i < orbOpts.length; i++) {
logBuf.append(orbOpts[i]).append(' ');
}
logBuf.append(" ORB properties: ");
logBuf.append(orbProps.toString());
m_logger.finer(logBuf.toString());
orb = org.omg.CORBA.ORB.init(orbOpts, orbProps);
if (orb == null) {
// seems like this should never happen, but got strange reports about NPE when archive MC creates Corba Any for alarms,
// so better check here and fail fast.
throw new NullPointerException("org.omg.CORBA.ORB.init returned null.");
}
m_logger.finer("ORB initialized.");
return orb;
}
// ==========================================================
// ORB - trial and error logic for finding a free port
// ==========================================================
protected class RVtrialAndError {
public ORB orb;
public int port;
public RVtrialAndError(ORB o, int p) {
orb = o;
port = p;
}
}
/** trial-and-error: try out some ports, break on success */
protected RVtrialAndError trialAndError(String[] args, int first, int retries) {
ORB orb = null;
int next = first;
int i;
for (i = 0; i < retries; i++) {
try {
next = first + i;
m_logger.info("trying to start an orb on port " + next);
orb = createOrb(args, new Integer(next));
initRootPoa(orb);
break;
} catch (RuntimeException exc) {
m_logger.finer("failed due to "+exc);
orb.shutdown(true);
continue;
}
}
if (i == retries) {
throw new RuntimeException("tried ports " + first + " to " + next + ": couldn't start orb; giving up");
}
return new RVtrialAndError(orb, next);
}
}