package alma.acs.daemontest; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import junit.framework.TestCase; import org.omg.CORBA.ORB; import org.omg.CORBA.TRANSIENT; import org.omg.CosNaming.NamingContext; import org.omg.CosNaming.NamingContextHelper; import alma.ACSErrTypeCommon.wrappers.AcsJUnexpectedExceptionEx; import alma.JavaContainerError.wrappers.AcsJContainerEx; import alma.acs.concurrent.DaemonThreadFactory; import alma.acs.container.corba.AcsCorba; import alma.acs.exceptions.AcsJCompletion; import alma.acs.logging.AcsLogger; import alma.acs.logging.ClientLogManager; import alma.acs.util.ACSPorts; import alma.acs.util.AcsLocations; import alma.acs.util.StopWatch; import alma.acsdaemon.DaemonCallback; import alma.acsdaemon.DaemonCallbackHelper; import alma.acsdaemon.DaemonSequenceCallback; import alma.acsdaemon.DaemonSequenceCallbackHelper; import alma.acsdaemon.ServiceDefinitionBuilder; import alma.acsdaemon.ServicesDaemon; import alma.acsdaemon.ServicesDaemonHelper; import alma.acsdaemon.systemNotificationServiceAlarms; import alma.acsdaemon.systemNotificationServiceDefault; import alma.acsdaemon.systemNotificationServiceLogging; import alma.acsdaemonErrType.ServiceAlreadyRunningEx; import alma.acsdaemonErrType.wrappers.AcsJServiceAlreadyRunningEx; /** * **** Work in progress, not in TAT yet! **** * <p> * Corba-related code copied from ComponentClientTestCase, * which we don't use here because it requires a running ACS with manager * which we don't need for the daemon test. * * @author hsommer */ public class ServicesDaemonTest extends TestCase { private static final String namePrefix = "ServicesDaemonTest"; private AcsCorba acsCorba; protected AcsLogger logger; private ServicesDaemon daemon; private String host; private short instanceNumber = (short) 4; //ACSPorts.getBasePort(); public ServicesDaemonTest() throws Exception { super(namePrefix); } ///////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// Infrastructural methods ////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// protected void runTest() throws Throwable { try { super.runTest(); } catch (Throwable thr) { if (logger != null) { logger.log(Level.WARNING, "JUnit test error in " + getFullName(), thr); } throw thr; } } protected String getFullName() { String fullName = namePrefix + "#" + getName(); return fullName; } protected void setUp() throws Exception { logger = ClientLogManager.getAcsLogManager().getLoggerForApplication(getFullName(), false); ClientLogManager.getAcsLogManager().suppressRemoteLogging(); logger.info("\n------------ " + getName() + " --------------"); try { acsCorba = new AcsCorba(logger); acsCorba.initCorbaForClient(false); } catch (Exception ex) { logger.log(Level.SEVERE, "failed to initialize the ORB", ex); if (acsCorba != null) { try { acsCorba.shutdownORB(true, false); acsCorba.doneCorba(); } catch (Exception ex2) { // to JUnit we want to forward the original exception, // not any other exception from cleaning up the orb, // which we would not have done without the first exception. ex2.printStackTrace(); } } throw ex; } host = ACSPorts.getIP(); //or "alma78.hq.eso.org" for Heiko on eclipse daemon = getServicesDaemon(host); } protected void tearDown() throws Exception { if (acsCorba != null) { acsCorba.shutdownORB(true, false); acsCorba.doneCorba(); } // just in case... should give the OS time to reclaim ORB ports and so on Thread.sleep(100); } private ServicesDaemon getServicesDaemon(String host) { String daemonLoc = AcsLocations.convertToServicesDaemonLocation(host); assertNotNull("corbaloc for service daemon must not be null", daemonLoc); logger.fine("Using services daemon corbaloc " + daemonLoc); ORB orb = acsCorba.getORB(); assertNotNull("ORB provided by inherited acsCorba must not be null", daemonLoc); org.omg.CORBA.Object obj = orb.string_to_object(daemonLoc); ServicesDaemon ret = null; try { ret = ServicesDaemonHelper.narrow(obj); } catch (TRANSIENT ex) { fail("Failed to get reference to services daemon '" + daemonLoc + "' because of TRANSIENT ex"); } assertNotNull("Corba ref to services daemon must not be null", ret); return ret; } private NamingContext getNamingService() { String cloc = "corbaloc::" + host + ":" + ACSPorts.globalInstance(instanceNumber).giveNamingServicePort() + "/NameService"; logger.info("Attempting to get naming service with " + cloc); org.omg.CORBA.Object objRef = acsCorba.getORB().string_to_object(cloc); return NamingContextHelper.narrow(objRef); } private void assertNamingService(boolean running) { try { NamingContext naming = getNamingService(); if (running) { logger.info("Got naming service as expected."); } else { fail("Expected TRANSIENT exception because naming service should not be running."); } } catch (Exception ex) { if (!running && ex instanceof TRANSIENT) { logger.info("Got expected org.omg.CORBA.TRANSIENT because naming service is not yet running."); } else { fail("Unexpected exception" + ex.toString()); } } } private void assertCDB() { String cloc = "corbaloc::" + host + ":" + ACSPorts.globalInstance(instanceNumber).giveCDBPort() + "/CDB"; logger.info("Attempting to get CDB with " + cloc); org.omg.CORBA.Object objRef = acsCorba.getORB().string_to_object(cloc); assertTrue(objRef._is_a("IDL:cosylab.com/CDB/DAL:1.0")); logger.info("Resolved CDB"); } private void assertManager() { String cloc = "corbaloc::" + host + ":" + ACSPorts.globalInstance(instanceNumber).giveManagerPort() + "/Manager"; logger.info("Attempting to get the manager with " + cloc); org.omg.CORBA.Object objRef = acsCorba.getORB().string_to_object(cloc); assertTrue(objRef._is_a("IDL:ijs.si/maci/Manager:1.0")); logger.info("Resolved the manager"); } /** * Creates a {@link DaemonCallbackImpl} object and registers it with the ORB. */ private DaemonCallback activateDaemonCallback(DaemonCallbackImpl daemonCallbackImpl) throws AcsJContainerEx, AcsJUnexpectedExceptionEx { DaemonCallback daemonCallback = DaemonCallbackHelper.narrow(acsCorba.activateOffShoot(daemonCallbackImpl, acsCorba.getRootPOA())); return daemonCallback; } /** * Creates a {@link DaemonCallbackImpl} object and registers it with the ORB. */ private DaemonSequenceCallback activateDaemonSequenceCallback(DaemonSequenceCallbackImpl callbackImpl) throws AcsJContainerEx, AcsJUnexpectedExceptionEx { DaemonSequenceCallback daemonSequenceCallback = DaemonSequenceCallbackHelper.narrow(acsCorba.activateOffShoot(callbackImpl, acsCorba.getRootPOA())); return daemonSequenceCallback; } ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////// Test methods ////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// /** * Starts and stops the naming service via the daemon, * checking the callbacks. */ public void testNamingSrvStartStopCheckCallback() throws Exception { assertNamingService(false); DaemonCallbackImpl daemonCallbackImpl = new DaemonCallbackImpl(logger); DaemonCallback dcb = activateDaemonCallback(daemonCallbackImpl); // start naming service daemonCallbackImpl.prepareWaitForDone("naming"); StopWatch sw = new StopWatch(logger); daemon.start_naming_service(dcb, instanceNumber); assertTrue("Failed to start naming service in 10 s", daemonCallbackImpl.waitForDone(10, TimeUnit.SECONDS)); sw.logLapTime("call start_naming_service"); assertFalse(AcsJCompletion.fromCorbaCompletion(daemonCallbackImpl.getLastDoneCompletion()).isError()); assertNamingService(true); // stop naming service daemonCallbackImpl.prepareWaitForDone("naming"); sw.reset(); daemon.stop_naming_service(dcb, instanceNumber); assertTrue("Failed to stop naming service in 5 s", daemonCallbackImpl.waitForDone(5, TimeUnit.SECONDS)); sw.logLapTime("call stop_naming_service"); } /** * Test starting service twice. It must return ServiceAlreadyRunning exception (immediately or via callback). */ public void testStartServiceTwice() throws Exception { DaemonCallbackImpl daemonCallbackImpl = new DaemonCallbackImpl(logger); DaemonCallback dcb = activateDaemonCallback(daemonCallbackImpl); // start naming service and wait till it's up daemonCallbackImpl.prepareWaitForDone("naming"); daemon.start_naming_service(dcb, instanceNumber); assertTrue("Failed to start naming service in 10 s", daemonCallbackImpl.waitForDone(10, TimeUnit.SECONDS)); assertNamingService(true); try { // now start it again daemonCallbackImpl.prepareWaitForDone("naming"); daemon.start_naming_service(dcb, instanceNumber); assertTrue("Failed to get reply about starting of second naming service within 10 s", daemonCallbackImpl.waitForDone(10, TimeUnit.SECONDS)); // error via callback? if (daemonCallbackImpl.getLastDoneCompletion().type != alma.ACSErr.acsdaemonErrType.value || daemonCallbackImpl.getLastDoneCompletion().code != alma.acsdaemonErrType.ServiceAlreadyRunning.value) fail("Expected ServiceAlreadyRunningEx when starting naming service twice."); } catch (ServiceAlreadyRunningEx ex) { // good logger.info("Got expected ServiceAlreadyRunningEx from attempting to start naming service twice."); assertEquals("Naming", AcsJServiceAlreadyRunningEx.fromServiceAlreadyRunningEx(ex).getService()); } finally { daemonCallbackImpl.prepareWaitForDone("naming"); daemon.stop_naming_service(dcb, instanceNumber); assertTrue("Failed to stop naming service in 5 s", daemonCallbackImpl.waitForDone(5, TimeUnit.SECONDS)); } } public void testStopServiceBeforeStarted() throws Exception { // TODO } /** * Simple test that only uses the start_xxx and Stop_xxx methods of the daemon, * which is one step up from the old acsStart method, but does not use the convenience * of the service description. All services are started on the same host. Later they are stopped. */ public void testStartAcsServiceIndividually() throws Throwable { DaemonCallbackImpl daemonCallbackImpl_1 = new DaemonCallbackImpl(logger); DaemonCallbackImpl daemonCallbackImpl_2 = new DaemonCallbackImpl(logger); DaemonCallbackImpl daemonCallbackImpl_3 = new DaemonCallbackImpl(logger); DaemonCallback dcb_1 = activateDaemonCallback(daemonCallbackImpl_1); DaemonCallback dcb_2 = activateDaemonCallback(daemonCallbackImpl_2); DaemonCallback dcb_3 = activateDaemonCallback(daemonCallbackImpl_3); List<Throwable> thrs = new ArrayList<Throwable>(); try { // start naming service and wait till it's up daemonCallbackImpl_1.prepareWaitForDone("naming"); daemon.start_naming_service(dcb_1, instanceNumber); assertTrue("Failed to start naming service in 10 s", daemonCallbackImpl_1.waitForDone(10, TimeUnit.SECONDS)); logger.info("Got naming service"); // start interface repository but don't wait for it yet ( start other services in parallel) daemonCallbackImpl_2.prepareWaitForDone("IFR"); daemon.start_interface_repository(true, true, dcb_2, instanceNumber); // start CDB and wait till it's up daemonCallbackImpl_1.prepareWaitForDone("CDB"); daemon.start_xml_cdb(dcb_1, instanceNumber, false, ""); // TODO try explicit path ($ACS_CDB replacement) assertTrue("Failed to start CDB in 15 s", daemonCallbackImpl_1.waitForDone(15, TimeUnit.SECONDS)); assertCDB(); // start manager and wait till it's up daemonCallbackImpl_1.prepareWaitForDone("manager"); daemon.start_manager("", dcb_1, instanceNumber, false); assertTrue("Failed to start the ACS manager in 10 s", daemonCallbackImpl_1.waitForDone(10, TimeUnit.SECONDS)); assertManager(); // now wait for the IR if necessary assertTrue("Failed to start interface repository 30 s after all other services have started", daemonCallbackImpl_2.waitForDone(30, TimeUnit.SECONDS)); logger.info("Got the IFR"); // start 3 of the 4 known notify services in parallel. // We want to call the start_notification_service method in parallel, which yields an even more parallel service start // than with calling this asynchronous method 3 times in a sequence. // @TODO Currently this test fails due to what seems a deadlock in the daemon. With just one NC factory it works, but with 3 we get a timeout. daemonCallbackImpl_1.prepareWaitForDone("NC factory default"); daemonCallbackImpl_2.prepareWaitForDone("NC factory logging"); daemonCallbackImpl_3.prepareWaitForDone("NC factory alarms"); class NotifySrvStarter implements Callable<Void> { private final String srvName; private final DaemonCallback cb; NotifySrvStarter(String srvName, DaemonCallback cb) { this.srvName = srvName; this.cb = cb; } public Void call() throws Exception { daemon.start_notification_service(srvName, cb, instanceNumber); return null; } } ExecutorService pool = Executors.newFixedThreadPool(3, new DaemonThreadFactory()); Future<Void> defaultNotifSrvFuture = pool.submit(new NotifySrvStarter(systemNotificationServiceDefault.value, dcb_1)); Future<Void> loggingNotifSrvFuture = pool.submit(new NotifySrvStarter(systemNotificationServiceLogging.value, dcb_2)); Future<Void> alarmNotifSrvFuture = pool.submit(new NotifySrvStarter(systemNotificationServiceAlarms.value, dcb_3)); try { defaultNotifSrvFuture.get(20, TimeUnit.SECONDS); loggingNotifSrvFuture.get(20, TimeUnit.SECONDS); alarmNotifSrvFuture.get(20, TimeUnit.SECONDS); } catch (ExecutionException ex) { // throw the ex that came from the call() method throw ex.getCause(); } catch (TimeoutException ex2) { fail("Failed to return from 'start_notification_service' within 20 seconds. "); } // now wait for the notification services to be actually started daemonCallbackImpl_1.waitForDone(10, TimeUnit.SECONDS); daemonCallbackImpl_2.waitForDone(10, TimeUnit.SECONDS); daemonCallbackImpl_3.waitForDone(10, TimeUnit.SECONDS); } catch (Throwable thr) { thrs.add(thr); } finally { // stop all services. Continue after errors to make sure the services are really stopped. // Stop the NC factories, calling the stop_notification_service methods one after the other // and then waiting for the asynch calls to finish. try { daemonCallbackImpl_1.prepareWaitForDone("stop NC factory default"); daemon.stop_notification_service(systemNotificationServiceDefault.value, dcb_1, instanceNumber); } catch (Throwable thr) { thrs.add(thr); } try { daemonCallbackImpl_2.prepareWaitForDone("stop NC factory logging"); daemon.stop_notification_service(systemNotificationServiceLogging.value, dcb_2, instanceNumber); } catch (Throwable thr) { thrs.add(thr); } try { daemonCallbackImpl_3.prepareWaitForDone("stop NC factory alarms"); daemon.stop_notification_service(systemNotificationServiceAlarms.value, dcb_3, instanceNumber); } catch (Throwable thr) { thrs.add(thr); } try { assertTrue("Failed to stop the default NC factory in 10 s", daemonCallbackImpl_1.waitForDone(10, TimeUnit.SECONDS)); assertTrue("Failed to stop the logging NC factory in 10 s", daemonCallbackImpl_2.waitForDone(10, TimeUnit.SECONDS)); assertTrue("Failed to stop the logging NC factory in 10 s", daemonCallbackImpl_3.waitForDone(10, TimeUnit.SECONDS)); } catch (Throwable thr) { thrs.add(thr); } // stop the IFR try { daemonCallbackImpl_1.prepareWaitForDone("stop IFR"); daemon.stop_interface_repository(dcb_1, instanceNumber); assertTrue("Failed to stop the interface repository in 10 s", daemonCallbackImpl_1.waitForDone(10, TimeUnit.SECONDS)); } catch (Throwable thr) { thrs.add(thr); } // stop the manager try { daemonCallbackImpl_1.prepareWaitForDone("stop manager"); daemon.stop_manager("", dcb_1, instanceNumber); assertTrue("Failed to stop the manager in 10 s", daemonCallbackImpl_1.waitForDone(10, TimeUnit.SECONDS)); } catch (Throwable thr) { thrs.add(thr); } } if (thrs.size() > 0) { logger.info("Failure: there were " + thrs.size() + " errors."); throw thrs.get(0); } } public void testServicesBuilder() throws Exception { ServiceDefinitionBuilder sdb = daemon.create_service_definition_builder(instanceNumber); assertNotNull(sdb); assertEquals("ACS instance number must match the one provided to the factory", instanceNumber, sdb.acs_instance_number()); // now we add services in an order that is not a legal startup order, to check re-ordering final String cdbPath = System.getProperty("user.dir"); sdb.add_notification_service(systemNotificationServiceDefault.value, host); sdb.add_manager(host, "", true); sdb.add_notification_service(systemNotificationServiceLogging.value, host); sdb.add_logging_service(host, ""); // "" means that default name "Log" should be used sdb.add_naming_service(host); sdb.add_xml_cdb(host, true, cdbPath); sdb.add_interface_repository(host, true, false); String xmlSrvDef = sdb.get_services_definition(); sdb.close(); System.out.println(xmlSrvDef); String ls = "\n"; //System.getProperty("line.separator"); String xmlExpected = "<acs_services_definition instance=\"" + instanceNumber + "\">" + ls + "<notification_service name=\"" + systemNotificationServiceDefault.value + "\" host=\"" + host + "\" />" + ls + "<manager host=\"" + host + "\" recovery=\"true\" />" + ls + "<notification_service name=\"" + systemNotificationServiceLogging.value + "\" host=\"" + host + "\" />" + ls + "<logging_service host=\"" + host + "\" />" + ls + "<naming_service host=\"" + host + "\" />" + ls + "<cdb host=\"" + host + "\" recovery=\"true\" cdb_xml_dir=\"" + cdbPath + "\" />" + ls + "<interface_repository host=\"" + host + "\" load=\"true\" wait_load=\"false\" />" + ls + "</acs_services_definition>" +ls; assertEquals(xmlExpected, xmlSrvDef); // Run these services DaemonSequenceCallbackImpl daemonSequenceCallbackImpl = new DaemonSequenceCallbackImpl(logger); DaemonSequenceCallback daemonSequenceCallback = activateDaemonSequenceCallback(daemonSequenceCallbackImpl); daemonSequenceCallbackImpl.prepareWaitForDone(); StopWatch sw = new StopWatch(logger); daemon.start_services(xmlSrvDef, false, daemonSequenceCallback); assertTrue("The services did not start in 2 minutes",daemonSequenceCallbackImpl.waitForDone(2, TimeUnit.MINUTES)); sw.logLapTime("call start_services"); // Stop the services daemonSequenceCallbackImpl.prepareWaitForDone(); sw = new StopWatch(logger); daemon.stop_services(xmlSrvDef,daemonSequenceCallback); assertTrue("The services did not stop in 2 minutes",daemonSequenceCallbackImpl.waitForDone(2, TimeUnit.MINUTES)); sw.logLapTime("call stop_services"); } }