// ********************************************************************** // // Copyright (c) 2003-2010 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** package IceBox; // // NOTE: the class isn't final on purpose to allow users to eventually // extend it. // public class ServiceManagerI extends _ServiceManagerDisp { public ServiceManagerI(Ice.Communicator communicator, String[] args) { _communicator = communicator; _logger = _communicator.getLogger(); _argv = args; _traceServiceObserver = _communicator.getProperties().getPropertyAsInt("IceBox.Trace.ServiceObserver"); } public java.util.Map<String, String> getSliceChecksums(Ice.Current current) { return SliceChecksums.checksums; } public void startService(String name, Ice.Current current) throws AlreadyStartedException, NoSuchServiceException { ServiceInfo info = null; synchronized(this) { // // Search would be more efficient if services were contained in // a map, but order is required for shutdown. // for(ServiceInfo p : _services) { if(p.name.equals(name)) { if(p.status == StatusStarted) { throw new AlreadyStartedException(); } p.status = StatusStarting; info = (ServiceInfo)p.clone(); break; } } if(info == null) { throw new NoSuchServiceException(); } _pendingStatusChanges = true; } boolean started = false; try { info.service.start(name, info.communicator == null ? _sharedCommunicator : info.communicator, info.args); started = true; } catch(java.lang.Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); e.printStackTrace(pw); pw.flush(); _logger.warning("ServiceManager: exception in start for service " + info.name + "\n" + sw.toString()); } synchronized(this) { for(ServiceInfo p : _services) { if(p.name.equals(name)) { if(started) { p.status = StatusStarted; java.util.List<String> services = new java.util.ArrayList<String>(); services.add(name); servicesStarted(services, _observers); } else { p.status = StatusStopped; } break; } } _pendingStatusChanges = false; notifyAll(); } } public void stopService(String name, Ice.Current current) throws AlreadyStoppedException, NoSuchServiceException { ServiceInfo info = null; synchronized(this) { // // Search would be more efficient if services were contained in // a map, but order is required for shutdown. // for(ServiceInfo p : _services) { if(p.name.equals(name)) { if(p.status == StatusStopped) { throw new AlreadyStoppedException(); } p.status = StatusStopping; info = (ServiceInfo)p.clone(); break; } } if(info == null) { throw new NoSuchServiceException(); } _pendingStatusChanges = true; } boolean stopped = false; try { info.service.stop(); stopped = true; } catch(java.lang.Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); e.printStackTrace(pw); pw.flush(); _logger.warning("ServiceManager: exception in stop for service " + info.name + "\n" + sw.toString()); } synchronized(this) { for(ServiceInfo p : _services) { if(p.name.equals(name)) { if(stopped) { p.status = StatusStopped; java.util.List<String> services = new java.util.ArrayList<String>(); services.add(name); servicesStopped(services, _observers); } else { p.status = StatusStarted; } break; } } _pendingStatusChanges = false; notifyAll(); } } public void addObserver(final ServiceObserverPrx observer, Ice.Current current) { java.util.List<String> activeServices = new java.util.LinkedList<String>(); // // Null observers and duplicate registrations are ignored // synchronized(this) { if(observer != null && _observers.add(observer)) { if(_traceServiceObserver >= 1) { _logger.trace("IceBox.ServiceObserver", "Added service observer " + _communicator.proxyToString(observer)); } for(ServiceInfo info: _services) { if(info.status == StatusStarted) { activeServices.add(info.name); } } } } if(activeServices.size() > 0) { AMI_ServiceObserver_servicesStarted cb = new AMI_ServiceObserver_servicesStarted() { public void ice_response() { // ok, success } public void ice_exception(Ice.LocalException ex) { // // Drop this observer // removeObserver(observer, ex); } }; observer.servicesStarted_async(cb, activeServices.toArray(new String[0])); } } public void shutdown(Ice.Current current) { _communicator.shutdown(); } public int run() { try { Ice.Properties properties = _communicator.getProperties(); // // Create an object adapter. Services probably should NOT share // this object adapter, as the endpoint(s) for this object adapter // will most likely need to be firewalled for security reasons. // Ice.ObjectAdapter adapter = null; if(!properties.getProperty("IceBox.ServiceManager.Endpoints").equals("")) { adapter = _communicator.createObjectAdapter("IceBox.ServiceManager"); Ice.Identity identity = new Ice.Identity(); identity.category = properties.getPropertyWithDefault("IceBox.InstanceName", "IceBox"); identity.name = "ServiceManager"; adapter.add(this, identity); } // // Parse the property set with the prefix "IceBox.Service.". These // properties should have the following format: // // IceBox.Service.Foo=Package.Foo [args] // // We parse the service properties specified in IceBox.LoadOrder // first, then the ones from remaining services. // final String prefix = "IceBox.Service."; java.util.Map<String, String> services = properties.getPropertiesForPrefix(prefix); String[] loadOrder = properties.getPropertyAsList("IceBox.LoadOrder"); java.util.List<StartServiceInfo> servicesInfo = new java.util.ArrayList<StartServiceInfo>(); for(String name : loadOrder) { if(name.length() > 0) { String key = prefix + name; String value = services.get(key); if(value == null) { FailureException ex = new FailureException(); ex.reason = "ServiceManager: no service definition for `" + name + "'"; throw ex; } servicesInfo.add(new StartServiceInfo(name, value, _argv)); services.remove(key); } } for(java.util.Map.Entry<String, String> p : services.entrySet()) { String name = p.getKey().substring(prefix.length()); String value = p.getValue(); servicesInfo.add(new StartServiceInfo(name, value, _argv)); } // // Check if some services are using the shared communicator in which // case we create the shared communicator now with a property set which // is the union of all the service properties (services which are using // the shared communicator). // if(properties.getPropertiesForPrefix("IceBox.UseSharedCommunicator.").size() > 0) { Ice.InitializationData initData = new Ice.InitializationData(); initData.properties = createServiceProperties("SharedCommunicator"); for(StartServiceInfo service : servicesInfo) { if(properties.getPropertyAsInt("IceBox.UseSharedCommunicator." + service.name) <= 0) { continue; } // // Load the service properties using the shared communicator properties as // the default properties. // Ice.StringSeqHolder serviceArgs = new Ice.StringSeqHolder(service.args); Ice.Properties svcProperties = Ice.Util.createProperties(serviceArgs, initData.properties); service.args = serviceArgs.value; // // Erase properties from the shared communicator which don't exist in the // service properties (which include the shared communicator properties // overriden by the service properties). // java.util.Map<String, String> allProps = initData.properties.getPropertiesForPrefix(""); for(String key : allProps.keySet()) { if(svcProperties.getProperty(key).length() == 0) { initData.properties.setProperty(key, ""); } } // // Add the service properties to the shared communicator properties. // for(java.util.Map.Entry<String, String> p : svcProperties.getPropertiesForPrefix("").entrySet()) { initData.properties.setProperty(p.getKey(), p.getValue()); } // // Parse <service>.* command line options (the Ice command line options // were parsed by the createProperties above) // service.args = initData.properties.parseCommandLineOptions(service.name, service.args); } _sharedCommunicator = Ice.Util.initialize(initData); } for(StartServiceInfo s : servicesInfo) { start(s.name, s.className, s.args); } // // We may want to notify external scripts that the services // have started. This is done by defining the property: // // IceBox.PrintServicesReady=bundleName // // Where bundleName is whatever you choose to call this set of // services. It will be echoed back as "bundleName ready". // // This must be done after start() has been invoked on the // services. // String bundleName = properties.getProperty("IceBox.PrintServicesReady"); if(bundleName.length() > 0) { System.out.println(bundleName + " ready"); } // // Don't move after the adapter activation. This allows // applications to wait for the service manager to be // reachable before sending a signal to shutdown the // IceBox. // Ice.Application.shutdownOnInterrupt(); // // Register "this" as a facet to the Admin object and // create Admin object // try { _communicator.addAdminFacet(this, "IceBox.ServiceManager"); // // Add a Properties facet for each service // for(ServiceInfo info: _services) { Ice.Communicator communicator = info.communicator != null ? info.communicator : _sharedCommunicator; _communicator.addAdminFacet(new PropertiesAdminI(communicator.getProperties()), "IceBox.Service." + info.name + ".Properties"); } _communicator.getAdmin(); } catch(Ice.ObjectAdapterDeactivatedException ex) { // // Expected if the communicator has been shutdown. // } // // Start request dispatching after we've started the services. // if(adapter != null) { try { adapter.activate(); } catch(Ice.ObjectAdapterDeactivatedException ex) { // // Expected if the communicator has been shutdown. // } } _communicator.waitForShutdown(); Ice.Application.defaultInterrupt(); // // Invoke stop() on the services. // stopAll(); } catch(FailureException ex) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); pw.println(ex.reason); ex.printStackTrace(pw); pw.flush(); _logger.error(sw.toString()); stopAll(); return 1; } catch(Ice.LocalException ex) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); ex.printStackTrace(pw); pw.flush(); _logger.error("ServiceManager: " + ex + "\n" + sw.toString()); stopAll(); return 1; } catch(java.lang.Exception ex) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); ex.printStackTrace(pw); pw.flush(); _logger.error("ServiceManager: unknown exception\n" + sw.toString()); stopAll(); return 1; } return 0; } synchronized private void start(String service, String className, String[] args) throws FailureException { // // Instantiate the class. // ServiceInfo info = new ServiceInfo(); info.name = service; info.status = StatusStopped; info.args = args; try { Class<?> c = IceInternal.Util.findClass(className, null); if(c == null) { FailureException e = new FailureException(); e.reason = "ServiceManager: class " + className + " not found"; throw e; } java.lang.Object obj = c.newInstance(); try { info.service = (Service)obj; } catch(ClassCastException ex) { FailureException e = new FailureException(); e.reason = "ServiceManager: class " + className + " does not implement IceBox.Service"; throw e; } } catch(IllegalAccessException ex) { FailureException e = new FailureException(); e.reason = "ServiceManager: unable to access default constructor in class " + className; e.initCause(ex); throw e; } catch(InstantiationException ex) { FailureException e = new FailureException(); e.reason = "ServiceManager: unable to instantiate class " + className; e.initCause(ex); throw e; } // // Invoke Service::start(). // try { // // If Ice.UseSharedCommunicator.<name> is defined, create a // communicator for the service. The communicator inherits // from the shared communicator properties. If it's not // defined, add the service properties to the shared // commnunicator property set. // Ice.Communicator communicator; if(_communicator.getProperties().getPropertyAsInt("IceBox.UseSharedCommunicator." + service) > 0) { assert(_sharedCommunicator != null); communicator = _sharedCommunicator; } else { // // Create the service properties. We use the communicator properties as the default // properties if IceBox.InheritProperties is set. // Ice.InitializationData initData = new Ice.InitializationData(); initData.properties = createServiceProperties(service); Ice.StringSeqHolder serviceArgs = new Ice.StringSeqHolder(info.args); if(serviceArgs.value.length > 0) { // // Create the service properties with the given service arguments. This should // read the service config file if it's specified with --Ice.Config. // initData.properties = Ice.Util.createProperties(serviceArgs, initData.properties); // // Next, parse the service "<service>.*" command line options (the Ice command // line options were parsed by the createProperties above) // serviceArgs.value = initData.properties.parseCommandLineOptions(service, serviceArgs.value); } // // Clone the logger to assign a new prefix. // initData.logger = _logger.cloneWithPrefix(initData.properties.getProperty("Ice.ProgramName")); // // Remaining command line options are passed to the communicator. This is // necessary for Ice plug-in properties (e.g.: IceSSL). // info.communicator = Ice.Util.initialize(serviceArgs, initData); info.args = serviceArgs.value; communicator = info.communicator; } try { info.service.start(service, communicator, info.args); info.status = StatusStarted; // // There is no need to notify the observers since the 'start all' // (that indirectly calls this method) occurs before the creation of // the Server Admin object, and before the activation of the main // object adapter (so before any observer can be registered) // } catch(Throwable ex) { if(info.communicator != null) { try { info.communicator.shutdown(); info.communicator.waitForShutdown(); } catch(Ice.CommunicatorDestroyedException e) { // // Ignore, the service might have already destroyed // the communicator for its own reasons. // } catch(java.lang.Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); e.printStackTrace(pw); pw.flush(); _logger.warning("ServiceManager: exception in shutting down communicator for service " + service + "\n" + sw.toString()); } try { info.communicator.destroy(); } catch(java.lang.Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); e.printStackTrace(pw); pw.flush(); _logger.warning("ServiceManager: exception in destroying communciator for service" + service + "\n" + sw.toString()); } } throw ex; } _services.add(info); } catch(FailureException ex) { throw ex; } catch(Throwable ex) { FailureException e = new FailureException(); e.reason = "ServiceManager: exception while starting service " + service + ": " + ex; e.initCause(ex); throw e; } } private synchronized void stopAll() { // // First wait for any active startService/stopService calls to complete. // while(_pendingStatusChanges) { try { wait(); } catch(java.lang.InterruptedException ex) { } } // // For each service, we call stop on the service and flush its database environment to // the disk. Services are stopped in the reverse order of the order they were started. // java.util.List<String> stoppedServices = new java.util.ArrayList<String>(); java.util.ListIterator<ServiceInfo> p = _services.listIterator(_services.size()); while(p.hasPrevious()) { ServiceInfo info = p.previous(); if(info.status == StatusStarted) { try { info.service.stop(); info.status = StatusStopped; stoppedServices.add(info.name); } catch(java.lang.Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); e.printStackTrace(pw); pw.flush(); _logger.warning("ServiceManager: exception in stop for service " + info.name + "\n" + sw.toString()); } } try { _communicator.removeAdminFacet("IceBox.Service." + info.name + ".Properties"); } catch(Ice.LocalException e) { // Ignored } if(info.communicator != null) { try { info.communicator.shutdown(); info.communicator.waitForShutdown(); } catch(Ice.CommunicatorDestroyedException e) { // // Ignore, the service might have already destroyed // the communicator for its own reasons. // } catch(java.lang.Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); e.printStackTrace(pw); pw.flush(); _logger.warning("ServiceManager: exception in stop for service " + info.name + "\n" + sw.toString()); } try { info.communicator.destroy(); } catch(java.lang.Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); e.printStackTrace(pw); pw.flush(); _logger.warning("ServiceManager: exception in stop for service " + info.name + "\n" + sw.toString()); } } } if(_sharedCommunicator != null) { try { _sharedCommunicator.destroy(); } catch(Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); java.io.PrintWriter pw = new java.io.PrintWriter(sw); e.printStackTrace(pw); pw.flush(); _logger.warning("ServiceManager: unknown exception while destroying shared communicator:\n" + sw.toString()); } _sharedCommunicator = null; } _services.clear(); servicesStopped(stoppedServices, _observers); } private void servicesStarted(java.util.List<String> services, java.util.Set<ServiceObserverPrx> observers) { if(services.size() > 0) { String[] servicesArray = services.toArray(new String[0]); for(final ServiceObserverPrx observer: observers) { AMI_ServiceObserver_servicesStarted cb = new AMI_ServiceObserver_servicesStarted() { public void ice_response() { // ok, success } public void ice_exception(Ice.LocalException ex) { // // Drop this observer // removeObserver(observer, ex); } }; observer.servicesStarted_async(cb, servicesArray); } } } private void servicesStopped(java.util.List<String> services, java.util.Set<ServiceObserverPrx> observers) { if(services.size() > 0) { String[] servicesArray = services.toArray(new String[0]); for(final ServiceObserverPrx observer: observers) { AMI_ServiceObserver_servicesStopped cb = new AMI_ServiceObserver_servicesStopped() { public void ice_response() { // ok, success } public void ice_exception(Ice.LocalException ex) { // // Drop this observer // removeObserver(observer, ex); } }; observer.servicesStopped_async(cb, servicesArray); } } } private synchronized void removeObserver(ServiceObserverPrx observer, Ice.LocalException ex) { if(_observers.remove(observer)) { observerRemoved(observer, ex); } } private void observerRemoved(ServiceObserverPrx observer, RuntimeException ex) { if(_traceServiceObserver >= 1) { // // CommunicatorDestroyedException may occur during shutdown. The observer notification has // been sent, but the communicator was destroyed before the reply was received. We do not // log a message for this exception. // if(!(ex instanceof Ice.CommunicatorDestroyedException)) { _logger.trace("IceBox.ServiceObserver", "Removed service observer " + _communicator.proxyToString(observer) + "\nafter catching " + ex.toString()); } } } public final static int StatusStopping = 0; public final static int StatusStopped = 1; public final static int StatusStarting = 2; public final static int StatusStarted = 3; static class ServiceInfo implements Cloneable { public Object clone() { Object o = null; try { o = super.clone(); } catch(CloneNotSupportedException ex) { } return o; } public String name; public Service service; public Ice.Communicator communicator = null; public int status; public String[] args; } static class PropertiesAdminI extends Ice._PropertiesAdminDisp { PropertiesAdminI(Ice.Properties properties) { _properties = properties; } public String getProperty(String name, Ice.Current current) { return _properties.getProperty(name); } public java.util.TreeMap<String, String> getPropertiesForPrefix(String name, Ice.Current current) { return new java.util.TreeMap<String, String>(_properties.getPropertiesForPrefix(name)); } private final Ice.Properties _properties; } static class StartServiceInfo { StartServiceInfo(String service, String value, String[] serverArgs) { name = service; // // Separate the entry point from the arguments. // int pos = IceUtilInternal.StringUtil.findFirstOf(value, " \t\n"); if(pos == -1) { className = value; args = new String[0]; } else { className = value.substring(0, pos); try { args = IceUtilInternal.Options.split(value.substring(pos)); } catch(IceUtilInternal.Options.BadQuote ex) { FailureException e = new FailureException(); e.reason = "ServiceManager: invalid arguments for service `" + name + "':\n" + ex.toString(); throw e; } } if(serverArgs.length > 0) { java.util.List<String> l = new java.util.ArrayList<String>(java.util.Arrays.asList(args)); for(String arg : serverArgs) { if(arg.startsWith("--" + service + ".")) { l.add(arg); } } args = l.toArray(args); } } String name; String[] args; String className; } private Ice.Properties createServiceProperties(String service) { Ice.Properties properties; Ice.Properties communicatorProperties = _communicator.getProperties(); if(communicatorProperties.getPropertyAsInt("IceBox.InheritProperties") > 0) { properties = communicatorProperties._clone(); properties.setProperty("Ice.Admin.Endpoints", ""); // Inherit all except Ice.Admin.Endpoints! } else { properties = Ice.Util.createProperties(); } String programName = communicatorProperties.getProperty("Ice.ProgramName"); if(programName.length() == 0) { properties.setProperty("Ice.ProgramName", service); } else { properties.setProperty("Ice.ProgramName", programName + "-" + service); } return properties; } private Ice.Communicator _communicator; private Ice.Communicator _sharedCommunicator; private Ice.Logger _logger; private String[] _argv; // Filtered server argument vector private java.util.List<ServiceInfo> _services = new java.util.LinkedList<ServiceInfo>(); private boolean _pendingStatusChanges = false; java.util.HashSet<ServiceObserverPrx> _observers = new java.util.HashSet<ServiceObserverPrx>(); int _traceServiceObserver = 0; }