package alma.acs.container.corba; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; import org.omg.PortableServer.POA; import org.omg.PortableServer.Servant; import alma.acs.container.ComponentServantManager; import alma.acs.logging.AcsLogger; import alma.acs.logging.ClientLogManager; import alma.jconttest.DummyComponent; import alma.jconttest.DummyComponentHelper; import alma.jconttest.DummyComponentPOATie; import alma.jconttest.DummyComponentImpl.DummyComponentImpl; /** * Tests {@link alma.acs.container.corba.AcsCorba} and related usage of ORB and POAs. * Runs without any ACS background support. * * @author hsommer */ public class AcsCorbaTest extends TestCase { private AcsLogger m_logger; private AcsCorba acsCorba; // simple otherthread-to-mainthread exception passing private volatile Throwable exceptionInThread; protected void setUp() throws Exception { exceptionInThread = null; m_logger = ClientLogManager.getAcsLogManager().getLoggerForApplication("AcsCorbaTest#" + getName(), false); ClientLogManager.getAcsLogManager().suppressRemoteLogging(); m_logger.info("-----------------------------------------------"); m_logger.info("AcsCorbaTest#setUp()"); // OrbConfigurator.setDebug(true); // will give detailed ORB/POA logs to stdout acsCorba = new AcsCorba(m_logger); acsCorba.initCorba(new String[0], OrbConfigurator.ORB_DEFAULT_PORT); } protected void tearDown() throws Exception { if (acsCorba != null) { acsCorba.shutdownORB(true, false); } } /** * Activates / deactivates a component 1000 times. * The component is run outside a container, but with the same ORB/POA usage as inside the container. * <p> * For each iteration, * <ol> * <li>component POA is created * <li>component implementation is created as the servant for our POA * <li>servant activator {@link ComponentServantManager} is created and registered with POA * <li>component is activated through component POA * <li>synchronous call to component method {@link DummyComponentImpl#dummyComponentsCanDoCloseToNothing()} over CORBA. * <li><b>synchronous</b> call to component method {@link DummyComponentImpl#callThatTakesSomeTime(int)} <b>with zero waiting time</b> over CORBA. * <li>call to {@link org.jacorb.poa.POA#destroy(boolean, boolean)} with <code>etherialize_objects==true</code> * and <code>wait_for_completion==false</code>. * <li>wait for component etherialization using {@link ComponentServantManager#waitForEtherealize(int)}. * </ol> * <p> * This method should be seen as a suggestion for the Java container to implement component deactivation. * @throws Exception * @see #_testComponentPOALifecycle(boolean, int) */ public void testComponentPOALifecycleSync() throws Exception { _testComponentPOALifecycle(false, 1000); } /** * This test method is currently hidden from JUnit because it fails with JacORB 1.4. and 2.2.4. * Problem: RequestController#waitForCompletion returns immediately when currently processing calls are local. * This could be related to JacORB bug 132, see http://www.jacorb.org/cgi-bin/bugzilla/show_bug.cgi?id=132. <br> * TODO: try again when the Java ORB gets updated or replaced in ACS. * <p> * Activates / deactivates a component 10 times, each time calling a method that still runs while * its POA is getting deactivated. This is to check the container's ability to wait for currently * processing requests to terminate, before <code>cleanUp</code> is called on the component. * <p> * The component is run outside a container, but with the same ORB/POA usage as inside the container. * @throws Exception */ public void __dontrun__testComponentPOALifecycleAsync() throws Exception { _testComponentPOALifecycle(true, 10); } /** * This test method can also be used to experiment with * @param destroyWhileBusy * @param iterations * @throws Exception */ private void _testComponentPOALifecycle(boolean destroyWhileBusy, int iterations) throws Exception { final String compName = "virtualTestComp"; for (int i=0; i < iterations; i++) { m_logger.info("Will create and destroy component instance #" + i); final POA compPOA = acsCorba.createPOAForComponent(compName); assertNotNull(compPOA); // create a test component servant using that POA final SyncDummyComponentImpl impl = new SyncDummyComponentImpl(); Servant servant = new DummyComponentPOATie(impl); final ComponentServantManager servantManager = acsCorba.setServantManagerOnComponentPOA(compPOA); // activate the component org.omg.CORBA.Object objRef = acsCorba.activateComponent(servant, compName, compPOA); // make a simple CORBA call to the component, and then destroy the POA final DummyComponent testCompRef = DummyComponentHelper.narrow(objRef); testCompRef.dummyComponentsCanDoCloseToNothing(); if (destroyWhileBusy) { final CountDownLatch sync = new CountDownLatch(1); impl.setMethodCallSync(sync); Runnable compMethodCallRunnable = new Runnable() { public void run() { try { testCompRef.callThatTakesSomeTime(1000); } catch (Exception ex) { exceptionInThread = ex; } } }; m_logger.info("Will destroy component POA while active request is still running."); (new Thread(compMethodCallRunnable)).start(); boolean properSync = sync.await(10000, TimeUnit.MILLISECONDS); assertTrue(properSync); } else { testCompRef.callThatTakesSomeTime(0); } // timeout should be larger than pending call (callThatTakesSomeTime) boolean isInactive = acsCorba.deactivateComponentPOAManager(compPOA, compName, 2000); assertTrue(isInactive); // active calls are supposedly over already, so the timeout can be smaller than execution time for "callThatTakesSomeTime" boolean isEtherealized = acsCorba.destroyComponentPOA(compPOA, servantManager, 500); assertTrue("Timeout here probably means that 'deactivateComponentPOAManager' did not properly wait for active requests to finish.", isEtherealized); if (exceptionInThread != null) { fail("asynchronous component call (#callThatTakesSomeTime) failed: " + exceptionInThread.toString()); } } m_logger.info("Done with testComponentPOALifecycle()"); } public void testPOAConfig() throws Exception { final String compName = "virtualTestComp"; POA compPOA = acsCorba.createPOAForComponent(compName); org.jacorb.poa.POA jacCompPOA = null; if (compPOA instanceof org.jacorb.poa.POA) { jacCompPOA = (org.jacorb.poa.POA) compPOA; } else { fail("this test is only meant for JacORB. Instead the POA impl is of type " + compPOA.getClass().getName()); } // Policy threadPolicy = jacCompPOA.getPolicy(THREAD_POLICY_ID.value); // assertNotNull(threadPolicy); // currently null, which defaults to ORB_CTRL_MODEL (see POA#sSingleThreadModel()) assertTrue(jacCompPOA.isUseServantManager()); String theName = jacCompPOA.the_name(); assertEquals("unexpected poa name ", "ComponentPOA_" + compName, theName); String qualName = jacCompPOA._getQualifiedName(); assertEquals("unexpected qualified poa name ", "ComponentPOA/ComponentPOA_" + compName, qualName); String poaId = new String(jacCompPOA.getPOAId()); assertEquals("unexpected poaId ", "StandardImplName/ComponentPOA/ComponentPOA_" + compName, poaId); // create a thread-aware test component servant using that POA DummyComponentImpl impl = new DummyComponentImpl() { public void dummyComponentsCanDoCloseToNothing() { Thread orbThread = Thread.currentThread(); System.out.println("component called in thread " + orbThread.getName()); } }; Servant servant = new DummyComponentPOATie(impl); // activate the component org.omg.CORBA.Object objRef = acsCorba.activateComponent(servant, compName, compPOA); // make CORBA calls to the component , and destroy the POA DummyComponent testCompRef = DummyComponentHelper.narrow(objRef); testCompRef.dummyComponentsCanDoCloseToNothing(); // analyze thread structure ThreadGroup rootThreadGroup = Thread.currentThread().getThreadGroup(); while (rootThreadGroup.getParent() != null) { rootThreadGroup = rootThreadGroup.getParent(); } ThreadGroup[] allThreadGroups = new ThreadGroup[rootThreadGroup.activeCount()*2]; // hopefully large enough int numThreadGroups = rootThreadGroup.enumerate(allThreadGroups, true); for (int i = 0; i < numThreadGroups; i++) { System.out.println("thread group " + allThreadGroups[i].getName() + ":"); Thread[] allThreadsInGroup = new Thread[allThreadGroups[i].activeCount()*2]; // hopefully large enough int numThreads = allThreadGroups[i].enumerate(allThreadsInGroup, false); for (int j = 0; j < numThreads; j++) { System.out.println("\t" + allThreadsInGroup[j].getName()); } } compPOA.destroy(false, true); } } /** * Special version of this component for local use. * The CountDownLatch set in <code>setMethodCallSync</code> can be used to synchronize between the client and * method invocations on the servant. This allows the client to block until the servant method is actually being executed. * @author hsommer */ class SyncDummyComponentImpl extends DummyComponentImpl { private volatile CountDownLatch sync; void setMethodCallSync(CountDownLatch sync) { this.sync = sync; } public void callThatTakesSomeTime(int timeInMillisec) { callSyncNotify(); super.callThatTakesSomeTime(timeInMillisec); } public void dummyComponentsCanDoCloseToNothing() { callSyncNotify(); super.dummyComponentsCanDoCloseToNothing(); } private void callSyncNotify() { if (sync != null) { sync.countDown(); } } }