1 // **********************************************************************
3 // Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
5 // This copy of Ice is licensed to you under the terms described in the
6 // ICE_LICENSE file included in this distribution.
8 // **********************************************************************
11 using System
.Collections
;
12 using System
.Collections
.Generic
;
13 using System
.Threading
;
14 using System
.Diagnostics
;
20 // NOTE: the class isn't final on purpose to allow users to eventually
23 class ServiceManagerI
: ServiceManagerDisp_
25 class AMIServicesStartedCallback
: AMI_ServiceObserver_servicesStarted
27 public AMIServicesStartedCallback(ServiceManagerI serviceManager
, ServiceObserverPrx observer
)
29 _serviceManager
= serviceManager
;
33 public override void ice_response()
38 public override void ice_exception(Ice
.Exception ex
)
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
;
61 public override void ice_response()
66 public override void ice_exception(Ice
.Exception ex
)
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();
83 _traceServiceObserver
= _communicator
.getProperties().getPropertyAsInt("IceBox.Trace.ServiceObserver");
86 public override Dictionary
<string, string>
87 getSliceChecksums(Ice
.Current current
)
89 return Ice
.SliceChecksums
.checksums
;
93 startService(string name
, Ice
.Current current
)
95 ServiceInfo info
= new ServiceInfo();
100 // Search would be more efficient if services were contained in
101 // a map, but order is required for shutdown.
104 for(i
= 0; i
< _services
.Count
; ++i
)
107 if(info
.name
.Equals(name
))
109 if(_services
[i
].status
!= ServiceStatus
.Stopped
)
111 throw new AlreadyStartedException();
113 info
.status
= ServiceStatus
.Starting
;
118 if(i
== _services
.Count
)
120 throw new NoSuchServiceException();
122 _pendingStatusChanges
= true;
129 bool started
= false;
132 info
.service
.start(info
.name
, info
.communicator
== null ? _sharedCommunicator
: info
.communicator
,
138 _logger
.warning("ServiceManager: exception while starting service " + info
.name
+ ":\n" + e
.ToString());
145 for(i
= 0; i
< _services
.Count
; ++i
)
148 if(info
.name
.Equals(name
))
152 info
.status
= ServiceStatus
.Started
;
154 List
<string> services
= new List
<string>();
156 servicesStarted(services
, _observers
.Keys
);
160 info
.status
= ServiceStatus
.Stopped
;
166 _pendingStatusChanges
= false;
176 stopService(string name
, Ice
.Current current
)
178 ServiceInfo info
= new ServiceInfo();
183 // Search would be more efficient if services were contained in
184 // a map, but order is required for shutdown.
187 for(i
= 0; i
< _services
.Count
; ++i
)
190 if(info
.name
.Equals(name
))
192 if(info
.status
!= ServiceStatus
.Started
)
194 throw new AlreadyStoppedException();
196 info
.status
= ServiceStatus
.Stopping
;
201 if(i
== _services
.Count
)
203 throw new NoSuchServiceException();
205 _pendingStatusChanges
= true;
212 bool stopped
= false;
220 _logger
.warning("ServiceManager: exception while stopping service " + info
.name
+ "\n" + e
.ToString());
227 for(i
= 0; i
< _services
.Count
; ++i
)
230 if(info
.name
.Equals(name
))
234 info
.status
= ServiceStatus
.Stopped
;
236 List
<string> services
= new List
<string>();
238 servicesStopped(services
, _observers
.Keys
);
242 info
.status
= ServiceStatus
.Started
;
248 _pendingStatusChanges
= false;
258 addObserver(ServiceObserverPrx observer
, Ice
.Current current
)
260 List
<string> activeServices
= new List
<string>();
263 // Null observers and duplicate registrations are ignored
273 _observers
.Add(observer
, true);
275 catch(ArgumentException
)
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
);
300 if(activeServices
.Count
> 0)
302 observer
.servicesStarted_async(new AMIServicesStartedCallback(this, observer
),
303 activeServices
.ToArray());
308 shutdown(Ice
.Current current
)
310 _communicator
.shutdown();
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
];
357 FailureException ex
= new FailureException();
358 ex
.reason
= "ServiceManager: no service definition for `" + loadOrder
[i
] + "'";
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)
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
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.
492 catch(Ice
.ObjectAdapterDeactivatedException
)
495 // Expected if the communicator has been shutdown.
500 _communicator
.waitForShutdown();
502 //Ice.Application.defaultInterrupt();
505 // Invoke stop() on the services.
509 catch(FailureException ex
)
511 _logger
.error(ex
.ToString());
517 _logger
.error("ServiceManager: caught exception:\n" + ex
.ToString());
526 startService(string service
, string entryPoint
, string[] args
)
531 ServiceInfo info
= new ServiceInfo();
533 info
.status
= ServiceStatus
.Stopped
;
537 // Retrieve the assembly name and the type.
539 string err
= "ServiceManager: unable to load service '" + entryPoint
+ "': ";
540 int sepPos
= entryPoint
.IndexOf(':');
543 if(entryPoint
.Length
> 3 &&
545 System
.Char
.IsLetter(entryPoint
[0]) &&
546 (entryPoint
[2] == '\\' || entryPoint
[2] == '/'))
548 sepPos
= entryPoint
.IndexOf(':', 3);
553 FailureException e
= new FailureException();
554 e
.reason
= err
+ "invalid entry point format: " + entryPoint
;
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
)
584 catch(System
.Exception ex
)
586 FailureException e
= new FailureException(ex
);
587 e
.reason
= err
+ "unable to load assembly: " + assemblyName
;
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
+ "'";
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
);
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)";
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
+ "'";
646 catch(System
.UnauthorizedAccessException ex
)
648 FailureException e
= new FailureException(ex
);
649 e
.reason
= err
+ "unauthorized access to default service constructor for " + className
;
654 catch(FailureException
)
658 catch(System
.InvalidCastException ex
)
660 FailureException e
= new FailureException(ex
);
661 e
.reason
= err
+ "service does not implement IceBox.Service";
664 catch(System
.Reflection
.TargetInvocationException ex
)
666 if(ex
.InnerException
is IceBox
.FailureException
)
668 throw ex
.InnerException
;
672 FailureException e
= new FailureException(ex
.InnerException
);
673 e
.reason
= "ServiceManager: exception in service constructor for " + className
;
677 catch(System
.Exception ex
)
679 FailureException e
= new FailureException(ex
);
680 e
.reason
= err
+ "exception in service constructor " + className
;
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
;
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());
780 catch(FailureException
)
784 catch(System
.Exception ex
)
786 FailureException e
= new FailureException(ex
);
787 e
.reason
= "ServiceManager: exception while starting service " + service
;
804 // First wait for any active startService/stopService calls to complete.
806 while(_pendingStatusChanges
)
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.
816 List
<string> stoppedServices
= new List
<string>();
817 foreach(ServiceInfo info
in _services
)
819 if(info
.status
== ServiceStatus
.Started
)
824 stoppedServices
.Add(info
.name
);
826 catch(System
.Exception e
)
828 _logger
.warning("IceBox.ServiceManager: exception while stopping service " + info
.name
+ ":\n" +
835 _communicator
.removeAdminFacet("IceBox.Service." + info
.name
+ ".Properties");
837 catch(Ice
.LocalException
)
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.
858 _logger
.warning("ServiceManager: exception while stopping service " + info
.name
+ ":\n" +
864 info
.communicator
.destroy();
866 catch(System
.Exception e
)
868 _logger
.warning("ServiceManager: exception while stopping service " + info
.name
+ ":\n" +
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;
888 servicesStopped(stoppedServices
, _observers
.Keys
);
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
);
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
);
935 removeObserver(ServiceObserverPrx observer
, Ice
.Exception ex
)
940 if(_observers
.Remove(observer
))
942 observerRemoved(observer
, ex
);
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
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.
996 args
= new string[0];
997 int start
= value.IndexOf(':');
1001 // Find the whitespace.
1003 int pos
= value.IndexOf(' ', start
);
1006 pos
= value.IndexOf('\t', start
);
1010 pos
= value.IndexOf('\n', start
);
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
+ "'";
1028 if(serverArgs
.Length
> 0)
1030 ArrayList l
= new ArrayList();
1031 for(int j
= 0; j
< args
.Length
; 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));
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!
1085 properties
= Ice
.Util
.createProperties();
1088 String programName
= communicatorProperties
.getProperty("Ice.ProgramName");
1089 if(programName
.Length
== 0)
1091 properties
.setProperty("Ice.ProgramName", service
);
1095 properties
.setProperty("Ice.ProgramName", programName
+ "-" + service
);
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();