ICE 3.4.2
[php5-ice-freebsdport.git] / cs / src / IceBox / ServiceManagerI.cs
blob47c3b63b5f7f534f1b61eb30f0787d3ad0acb59e
1 // **********************************************************************
2 //
3 // Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
4 //
5 // This copy of Ice is licensed to you under the terms described in the
6 // ICE_LICENSE file included in this distribution.
7 //
8 // **********************************************************************
10 using System;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Threading;
14 using System.Diagnostics;
16 namespace IceBox
20 // NOTE: the class isn't final on purpose to allow users to eventually
21 // extend it.
23 class ServiceManagerI : ServiceManagerDisp_
25 class AMIServicesStartedCallback : AMI_ServiceObserver_servicesStarted
27 public AMIServicesStartedCallback(ServiceManagerI serviceManager, ServiceObserverPrx observer)
29 _serviceManager = serviceManager;
30 _observer = observer;
33 public override void ice_response()
35 // ok, success
38 public override void ice_exception(Ice.Exception ex)
41 // Drop this observer
43 _serviceManager.removeObserver(_observer, ex);
46 private ServiceManagerI _serviceManager;
47 private ServiceObserverPrx _observer;
51 // TODO: would be nice to avoid the duplication AMIServicesStartedCallback/AMIServicesStoppedCallback
53 class AMIServicesStoppedCallback : AMI_ServiceObserver_servicesStopped
55 public AMIServicesStoppedCallback(ServiceManagerI serviceManager, ServiceObserverPrx observer)
57 _serviceManager = serviceManager;
58 _observer = observer;
61 public override void ice_response()
63 // ok, success
66 public override void ice_exception(Ice.Exception ex)
69 // Drop this observer
71 _serviceManager.removeObserver(_observer, ex);
74 private ServiceManagerI _serviceManager;
75 private ServiceObserverPrx _observer;
78 public ServiceManagerI(Ice.Communicator communicator, string[] args)
80 _communicator = communicator;
81 _logger = _communicator.getLogger();
82 _argv = args;
83 _traceServiceObserver = _communicator.getProperties().getPropertyAsInt("IceBox.Trace.ServiceObserver");
86 public override Dictionary<string, string>
87 getSliceChecksums(Ice.Current current)
89 return Ice.SliceChecksums.checksums;
92 public override void
93 startService(string name, Ice.Current current)
95 ServiceInfo info = new ServiceInfo();
96 _m.Lock();
97 try
100 // Search would be more efficient if services were contained in
101 // a map, but order is required for shutdown.
103 int i;
104 for(i = 0; i < _services.Count; ++i)
106 info = _services[i];
107 if(info.name.Equals(name))
109 if(_services[i].status != ServiceStatus.Stopped)
111 throw new AlreadyStartedException();
113 info.status = ServiceStatus.Starting;
114 _services[i] = info;
115 break;
118 if(i == _services.Count)
120 throw new NoSuchServiceException();
122 _pendingStatusChanges = true;
124 finally
126 _m.Unlock();
129 bool started = false;
132 info.service.start(info.name, info.communicator == null ? _sharedCommunicator : info.communicator,
133 info.args);
134 started = true;
136 catch(Exception e)
138 _logger.warning("ServiceManager: exception while starting service " + info.name + ":\n" + e.ToString());
141 _m.Lock();
144 int i;
145 for(i = 0; i < _services.Count; ++i)
147 info = _services[i];
148 if(info.name.Equals(name))
150 if(started)
152 info.status = ServiceStatus.Started;
154 List<string> services = new List<string>();
155 services.Add(name);
156 servicesStarted(services, _observers.Keys);
158 else
160 info.status = ServiceStatus.Stopped;
162 _services[i] = info;
163 break;
166 _pendingStatusChanges = false;
167 _m.NotifyAll();
169 finally
171 _m.Unlock();
175 public override void
176 stopService(string name, Ice.Current current)
178 ServiceInfo info = new ServiceInfo();
179 _m.Lock();
183 // Search would be more efficient if services were contained in
184 // a map, but order is required for shutdown.
186 int i;
187 for(i = 0; i < _services.Count; ++i)
189 info = _services[i];
190 if(info.name.Equals(name))
192 if(info.status != ServiceStatus.Started)
194 throw new AlreadyStoppedException();
196 info.status = ServiceStatus.Stopping;
197 _services[i] = info;
198 break;
201 if(i == _services.Count)
203 throw new NoSuchServiceException();
205 _pendingStatusChanges = true;
207 finally
209 _m.Unlock();
212 bool stopped = false;
215 info.service.stop();
216 stopped = true;
218 catch(Exception e)
220 _logger.warning("ServiceManager: exception while stopping service " + info.name + "\n" + e.ToString());
223 _m.Lock();
226 int i;
227 for(i = 0; i < _services.Count; ++i)
229 info = _services[i];
230 if(info.name.Equals(name))
232 if(stopped)
234 info.status = ServiceStatus.Stopped;
236 List<string> services = new List<string>();
237 services.Add(name);
238 servicesStopped(services, _observers.Keys);
240 else
242 info.status = ServiceStatus.Started;
244 _services[i] = info;
245 break;
248 _pendingStatusChanges = false;
249 _m.NotifyAll();
251 finally
253 _m.Unlock();
257 public override void
258 addObserver(ServiceObserverPrx observer, Ice.Current current)
260 List<string> activeServices = new List<string>();
263 // Null observers and duplicate registrations are ignored
266 _m.Lock();
269 if(observer != null)
273 _observers.Add(observer, true);
275 catch(ArgumentException)
277 return;
280 if(_traceServiceObserver >= 1)
282 _logger.trace("IceBox.ServiceObserver",
283 "Added service observer " + _communicator.proxyToString(observer));
286 foreach(ServiceInfo info in _services)
288 if(info.status == ServiceStatus.Started)
290 activeServices.Add(info.name);
295 finally
297 _m.Unlock();
300 if(activeServices.Count > 0)
302 observer.servicesStarted_async(new AMIServicesStartedCallback(this, observer),
303 activeServices.ToArray());
307 public override void
308 shutdown(Ice.Current current)
310 _communicator.shutdown();
313 public int
314 run()
318 Ice.Properties properties = _communicator.getProperties();
321 // Create an object adapter. Services probably should NOT share
322 // this object adapter, as the endpoint(s) for this object adapter
323 // will most likely need to be firewalled for security reasons.
325 Ice.ObjectAdapter adapter = null;
326 if(properties.getProperty("IceBox.ServiceManager.Endpoints").Length != 0)
328 adapter = _communicator.createObjectAdapter("IceBox.ServiceManager");
330 Ice.Identity identity = new Ice.Identity();
331 identity.category = properties.getPropertyWithDefault("IceBox.InstanceName", "IceBox");
332 identity.name = "ServiceManager";
333 adapter.add(this, identity);
337 // Parse the property set with the prefix "IceBox.Service.". These
338 // properties should have the following format:
340 // IceBox.Service.Foo=Package.Foo [args]
342 // We parse the service properties specified in IceBox.LoadOrder
343 // first, then the ones from remaining services.
345 string prefix = "IceBox.Service.";
346 Dictionary<string, string> services = properties.getPropertiesForPrefix(prefix);
347 string[] loadOrder = properties.getPropertyAsList("IceBox.LoadOrder");
348 List<StartServiceInfo> servicesInfo = new List<StartServiceInfo>();
349 for(int i = 0; i < loadOrder.Length; ++i)
351 if(loadOrder[i].Length > 0)
353 string key = prefix + loadOrder[i];
354 string value = services[key];
355 if(value == null)
357 FailureException ex = new FailureException();
358 ex.reason = "ServiceManager: no service definition for `" + loadOrder[i] + "'";
359 throw ex;
361 servicesInfo.Add(new StartServiceInfo(loadOrder[i], value, _argv));
362 services.Remove(key);
365 foreach(KeyValuePair<string, string> entry in services)
367 string name = entry.Key.Substring(prefix.Length);
368 string value = entry.Value;
369 servicesInfo.Add(new StartServiceInfo(name, value, _argv));
373 // Check if some services are using the shared communicator in which
374 // case we create the shared communicator now with a property set which
375 // is the union of all the service properties (services which are using
376 // the shared communicator).
378 if(properties.getPropertiesForPrefix("IceBox.UseSharedCommunicator.").Count > 0)
380 Ice.InitializationData initData = new Ice.InitializationData();
381 initData.properties = createServiceProperties("SharedCommunicator");
382 foreach(StartServiceInfo service in servicesInfo)
384 if(properties.getPropertyAsInt("IceBox.UseSharedCommunicator." + service.name) <= 0)
386 continue;
390 // Load the service properties using the shared communicator properties as
391 // the default properties.
393 Ice.Properties svcProperties = Ice.Util.createProperties(ref service.args, initData.properties);
396 // Erase properties from the shared communicator which don't exist in the
397 // service properties (which include the shared communicator properties
398 // overriden by the service properties).
400 Dictionary<string, string> allProps = initData.properties.getPropertiesForPrefix("");
401 foreach(string key in allProps.Keys)
403 if(svcProperties.getProperty(key).Length == 0)
405 initData.properties.setProperty(key, "");
410 // Add the service properties to the shared communicator properties.
412 foreach(KeyValuePair<string, string> entry in svcProperties.getPropertiesForPrefix(""))
414 initData.properties.setProperty(entry.Key, entry.Value);
418 // Parse <service>.* command line options (the Ice command line options
419 // were parsed by the createProperties above)
421 service.args = initData.properties.parseCommandLineOptions(service.name, service.args);
423 _sharedCommunicator = Ice.Util.initialize(initData);
426 foreach(StartServiceInfo s in servicesInfo)
428 startService(s.name, s.entryPoint, s.args);
432 // We may want to notify external scripts that the services
433 // have started. This is done by defining the property:
435 // PrintServicesReady=bundleName
437 // Where bundleName is whatever you choose to call this set of
438 // services. It will be echoed back as "bundleName ready".
440 // This must be done after start() has been invoked on the
441 // services.
443 string bundleName = properties.getProperty("IceBox.PrintServicesReady");
444 if(bundleName.Length > 0)
446 Console.Out.WriteLine(bundleName + " ready");
450 // Don't move after the adapter activation. This allows
451 // applications to wait for the service manager to be
452 // reachable before sending a signal to shutdown the
455 Ice.Application.shutdownOnInterrupt();
458 // Register "this" as a facet to the Admin object and create Admin object
462 _communicator.addAdminFacet(this, "IceBox.ServiceManager");
465 // Add a Properties facet for each service
467 foreach(ServiceInfo info in _services)
469 Ice.Communicator communicator = info.communicator != null ? info.communicator : _sharedCommunicator;
470 _communicator.addAdminFacet(new PropertiesAdminI(communicator.getProperties()),
471 "IceBox.Service." + info.name + ".Properties");
474 _communicator.getAdmin();
476 catch(Ice.ObjectAdapterDeactivatedException)
479 // Expected if the communicator has been shutdown.
484 // Start request dispatching after we've started the services.
486 if(adapter != null)
490 adapter.activate();
492 catch(Ice.ObjectAdapterDeactivatedException)
495 // Expected if the communicator has been shutdown.
500 _communicator.waitForShutdown();
501 // XXX:
502 //Ice.Application.defaultInterrupt();
505 // Invoke stop() on the services.
507 stopAll();
509 catch(FailureException ex)
511 _logger.error(ex.ToString());
512 stopAll();
513 return 1;
515 catch(Exception ex)
517 _logger.error("ServiceManager: caught exception:\n" + ex.ToString());
518 stopAll();
519 return 1;
522 return 0;
525 private void
526 startService(string service, string entryPoint, string[] args)
528 _m.Lock();
531 ServiceInfo info = new ServiceInfo();
532 info.name = service;
533 info.status = ServiceStatus.Stopped;
534 info.args = args;
537 // Retrieve the assembly name and the type.
539 string err = "ServiceManager: unable to load service '" + entryPoint + "': ";
540 int sepPos = entryPoint.IndexOf(':');
541 if(sepPos != -1)
543 if(entryPoint.Length > 3 &&
544 sepPos == 1 &&
545 System.Char.IsLetter(entryPoint[0]) &&
546 (entryPoint[2] == '\\' || entryPoint[2] == '/'))
548 sepPos = entryPoint.IndexOf(':', 3);
551 if(sepPos == -1)
553 FailureException e = new FailureException();
554 e.reason = err + "invalid entry point format: " + entryPoint;
555 throw e;
558 System.Reflection.Assembly serviceAssembly = null;
559 string assemblyName = entryPoint.Substring(0, sepPos);
563 // First try to load the assemby using Assembly.Load which will succeed
564 // if full name is configured or partial name has been qualified in config.
565 // If that fails, try Assembly.LoadFrom() which will succeed if a file name
566 // is configured or partial name is configured and DEVPATH is used.
570 serviceAssembly = System.Reflection.Assembly.Load(assemblyName);
572 catch(System.IO.IOException ex)
576 serviceAssembly = System.Reflection.Assembly.LoadFrom(assemblyName);
578 catch(System.IO.IOException)
580 throw ex;
584 catch(System.Exception ex)
586 FailureException e = new FailureException(ex);
587 e.reason = err + "unable to load assembly: " + assemblyName;
588 throw e;
592 // Instantiate the class.
594 string className = entryPoint.Substring(sepPos + 1);
595 System.Type c = null;
598 c = serviceAssembly.GetType(className, true);
600 catch(System.Exception ex)
602 FailureException e = new FailureException(ex);
603 e.reason = err + "GetType failed for '" + className + "'";
604 throw e;
610 // If the service class provides a constructor that accepts an Ice.Communicator argument,
611 // use that in preference to the default constructor.
613 Type[] parameterTypes = new Type[1];
614 parameterTypes[0] = typeof(Ice.Communicator);
615 System.Reflection.ConstructorInfo ci = c.GetConstructor(parameterTypes);
616 if(ci != null)
620 Object[] parameters = new Object[1];
621 parameters[0] = _communicator;
622 info.service = (Service)ci.Invoke(parameters);
624 catch(System.MethodAccessException ex)
626 FailureException e = new FailureException(ex);
627 e.reason = err + "unable to access service constructor " + className + "(Ice.Communicator)";
628 throw e;
631 else
634 // Fall back to the default constructor.
638 info.service = (Service)IceInternal.AssemblyUtil.createInstance(c);
639 if(info.service == null)
641 FailureException e = new FailureException();
642 e.reason = err + "no default constructor for '" + className + "'";
643 throw e;
646 catch(System.UnauthorizedAccessException ex)
648 FailureException e = new FailureException(ex);
649 e.reason = err + "unauthorized access to default service constructor for " + className;
650 throw e;
654 catch(FailureException)
656 throw;
658 catch(System.InvalidCastException ex)
660 FailureException e = new FailureException(ex);
661 e.reason = err + "service does not implement IceBox.Service";
662 throw e;
664 catch(System.Reflection.TargetInvocationException ex)
666 if(ex.InnerException is IceBox.FailureException)
668 throw ex.InnerException;
670 else
672 FailureException e = new FailureException(ex.InnerException);
673 e.reason = "ServiceManager: exception in service constructor for " + className;
674 throw e;
677 catch(System.Exception ex)
679 FailureException e = new FailureException(ex);
680 e.reason = err + "exception in service constructor " + className;
681 throw e;
685 // Invoke Service::start().
690 // If IceBox.UseSharedCommunicator.<name> is defined, create a
691 // communicator for the service. The communicator inherits
692 // from the shared communicator properties. If it's not
693 // defined, add the service properties to the shared
694 // commnunicator property set.
696 Ice.Communicator communicator;
697 if(_communicator.getProperties().getPropertyAsInt("IceBox.UseSharedCommunicator." + service) > 0)
699 Debug.Assert(_sharedCommunicator != null);
700 communicator = _sharedCommunicator;
702 else
705 // Create the service properties. We use the communicator properties as the default
706 // properties if IceBox.InheritProperties is set.
708 Ice.InitializationData initData = new Ice.InitializationData();
709 initData.properties = createServiceProperties(service);
710 if(info.args.Length > 0)
713 // Create the service properties with the given service arguments. This should
714 // read the service config file if it's specified with --Ice.Config.
716 initData.properties = Ice.Util.createProperties(ref info.args, initData.properties);
719 // Next, parse the service "<service>.*" command line options (the Ice command
720 // line options were parsed by the createProperties above)
722 info.args = initData.properties.parseCommandLineOptions(service, info.args);
726 // Clone the logger to assign a new prefix.
728 initData.logger = _logger.cloneWithPrefix(initData.properties.getProperty("Ice.ProgramName"));
731 // Remaining command line options are passed to the communicator. This is
732 // necessary for Ice plug-in properties (e.g.: IceSSL).
734 info.communicator = Ice.Util.initialize(ref info.args, initData);
735 communicator = info.communicator;
740 info.service.start(service, communicator, info.args);
741 info.status = ServiceStatus.Started;
743 catch(System.Exception)
745 if(info.communicator != null)
749 info.communicator.shutdown();
750 info.communicator.waitForShutdown();
752 catch(Ice.CommunicatorDestroyedException)
755 // Ignore, the service might have already destroyed
756 // the communicator for its own reasons.
759 catch(System.Exception e)
761 _logger.warning("ServiceManager: exception while shutting down communicator for service "
762 + service + "\n" + e.ToString());
767 info.communicator.destroy();
769 catch(System.Exception e)
771 _logger.warning("ServiceManager: exception while destroying communicator for service "
772 + service + "\n" + e.ToString());
775 throw;
778 _services.Add(info);
780 catch(FailureException)
782 throw;
784 catch(System.Exception ex)
786 FailureException e = new FailureException(ex);
787 e.reason = "ServiceManager: exception while starting service " + service;
788 throw e;
791 finally
793 _m.Unlock();
797 private void
798 stopAll()
800 _m.Lock();
804 // First wait for any active startService/stopService calls to complete.
806 while(_pendingStatusChanges)
808 _m.Wait();
812 // First, for each service, we call stop on the service and flush its database environment to
813 // the disk. Services are stopped in the reverse order of which they were started.
815 _services.Reverse();
816 List<string> stoppedServices = new List<string>();
817 foreach(ServiceInfo info in _services)
819 if(info.status == ServiceStatus.Started)
823 info.service.stop();
824 stoppedServices.Add(info.name);
826 catch(System.Exception e)
828 _logger.warning("IceBox.ServiceManager: exception while stopping service " + info.name + ":\n" +
829 e.ToString());
835 _communicator.removeAdminFacet("IceBox.Service." + info.name + ".Properties");
837 catch(Ice.LocalException)
839 // Ignored
842 if(info.communicator != null)
846 info.communicator.shutdown();
847 info.communicator.waitForShutdown();
849 catch(Ice.CommunicatorDestroyedException)
852 // Ignore, the service might have already destroyed
853 // the communicator for its own reasons.
856 catch(Exception e)
858 _logger.warning("ServiceManager: exception while stopping service " + info.name + ":\n" +
859 e.ToString());
864 info.communicator.destroy();
866 catch(System.Exception e)
868 _logger.warning("ServiceManager: exception while stopping service " + info.name + ":\n" +
869 e.ToString());
874 if(_sharedCommunicator != null)
878 _sharedCommunicator.destroy();
880 catch(System.Exception e)
882 _logger.warning("ServiceManager: exception while destroying shared communicator:\n" + e.ToString());
884 _sharedCommunicator = null;
887 _services.Clear();
888 servicesStopped(stoppedServices, _observers.Keys);
890 finally
892 _m.Unlock();
896 private void
897 servicesStarted(List<String> services, Dictionary<ServiceObserverPrx, bool>.KeyCollection observers)
900 // Must be called with 'this' unlocked
903 if(services.Count > 0)
905 string[] servicesArray = services.ToArray();
907 foreach(ServiceObserverPrx observer in observers)
909 AMI_ServiceObserver_servicesStarted cb = new AMIServicesStartedCallback(this, observer);
910 observer.servicesStarted_async(cb, servicesArray);
915 private void
916 servicesStopped(List<string> services, Dictionary<ServiceObserverPrx, bool>.KeyCollection observers)
919 // Must be called with 'this' unlocked
922 if(services.Count > 0)
924 string[] servicesArray = services.ToArray();
926 foreach(ServiceObserverPrx observer in observers)
928 AMI_ServiceObserver_servicesStopped cb = new AMIServicesStoppedCallback(this, observer);
929 observer.servicesStopped_async(cb, servicesArray);
934 private void
935 removeObserver(ServiceObserverPrx observer, Ice.Exception ex)
937 _m.Lock();
940 if(_observers.Remove(observer))
942 observerRemoved(observer, ex);
945 finally
947 _m.Unlock();
951 private void
952 observerRemoved(ServiceObserverPrx observer, System.Exception ex)
954 if(_traceServiceObserver >= 1)
957 // CommunicatorDestroyedException may occur during shutdown. The observer notification has
958 // been sent, but the communicator was destroyed before the reply was received. We do not
959 // log a message for this exception.
961 if(!(ex is Ice.CommunicatorDestroyedException))
963 _logger.trace("IceBox.ServiceObserver",
964 "Removed service observer " + _communicator.proxyToString(observer)
965 + "\nafter catching " + ex.ToString());
970 private enum ServiceStatus
972 Stopping,
973 Stopped,
974 Starting,
975 Started
978 struct ServiceInfo
980 public string name;
981 public Service service;
982 public Ice.Communicator communicator;
983 public ServiceStatus status;
984 public string[] args;
987 class StartServiceInfo
989 public StartServiceInfo(string service, string value, string[] serverArgs)
992 // Separate the entry point from the arguments.
994 name = service;
995 entryPoint = value;
996 args = new string[0];
997 int start = value.IndexOf(':');
998 if(start != -1)
1001 // Find the whitespace.
1003 int pos = value.IndexOf(' ', start);
1004 if(pos == -1)
1006 pos = value.IndexOf('\t', start);
1008 if(pos == -1)
1010 pos = value.IndexOf('\n', start);
1012 if(pos != -1)
1014 entryPoint = value.Substring(0, pos);
1017 args = IceUtilInternal.Options.split(value.Substring(pos));
1019 catch(IceUtilInternal.Options.BadQuote ex)
1021 FailureException e = new FailureException(ex);
1022 e.reason = "ServiceManager: invalid arguments for service `" + name + "'";
1023 throw e;
1028 if(serverArgs.Length > 0)
1030 ArrayList l = new ArrayList();
1031 for(int j = 0; j < args.Length; j++)
1033 l.Add(args[j]);
1035 for(int j = 0; j < serverArgs.Length; j++)
1037 if(serverArgs[j].StartsWith("--" + service + ".", StringComparison.Ordinal))
1039 l.Add(serverArgs[j]);
1042 args = (string[])l.ToArray(typeof(string));
1046 public string name;
1047 public string entryPoint;
1048 public string[] args;
1051 class PropertiesAdminI : Ice.PropertiesAdminDisp_
1053 public PropertiesAdminI(Ice.Properties properties)
1055 _properties = properties;
1058 public override string
1059 getProperty(string name, Ice.Current current)
1061 return _properties.getProperty(name);
1064 public override Dictionary<string, string>
1065 getPropertiesForPrefix(string name, Ice.Current current)
1067 return _properties.getPropertiesForPrefix(name);
1070 private Ice.Properties _properties;
1073 private Ice.Properties
1074 createServiceProperties(String service)
1076 Ice.Properties properties;
1077 Ice.Properties communicatorProperties = _communicator.getProperties();
1078 if(communicatorProperties.getPropertyAsInt("IceBox.InheritProperties") > 0)
1080 properties = communicatorProperties.ice_clone_();
1081 properties.setProperty("Ice.Admin.Endpoints", ""); // Inherit all except Ice.Admin.Endpoints!
1083 else
1085 properties = Ice.Util.createProperties();
1088 String programName = communicatorProperties.getProperty("Ice.ProgramName");
1089 if(programName.Length == 0)
1091 properties.setProperty("Ice.ProgramName", service);
1093 else
1095 properties.setProperty("Ice.ProgramName", programName + "-" + service);
1097 return properties;
1100 private Ice.Communicator _communicator;
1101 private Ice.Communicator _sharedCommunicator = null;
1102 private Ice.Logger _logger;
1103 private string[] _argv; // Filtered server argument vector
1104 private List<ServiceInfo> _services = new List<ServiceInfo>();
1105 private bool _pendingStatusChanges = false;
1106 private Dictionary<ServiceObserverPrx, bool> _observers = new Dictionary<ServiceObserverPrx, bool>();
1107 private int _traceServiceObserver = 0;
1108 private readonly IceUtilInternal.Monitor _m = new IceUtilInternal.Monitor();