< Summary

Information
Class: IceBox.ServiceManagerI.StartServiceInfo
Assembly: iceboxnet
File(s): /_/csharp/src/iceboxnet/ServiceManagerI.cs
Tag: 99_23991109993
Line coverage
52%
Covered lines: 10
Uncovered lines: 9
Coverable lines: 19
Total lines: 1027
Line coverage: 52.6%
Branch coverage
12%
Covered branches: 1
Total branches: 8
Branch coverage: 12.5%
Method coverage
100%
Covered methods: 1
Total methods: 1
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)12.5%14.8852.63%

File(s)

/_/csharp/src/iceboxnet/ServiceManagerI.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Collections;
 4using System.Diagnostics;
 5
 6namespace IceBox;
 7
 8internal class ServiceManagerI : ServiceManagerDisp_, IDisposable
 9{
 10    public ServiceManagerI(Ice.Communicator communicator, string[] args)
 11    {
 12        _communicator = communicator;
 13        _logger = _communicator.getLogger();
 14
 15        Ice.Properties props = _communicator.getProperties();
 16
 17        if (props.getIceProperty("Ice.Admin.Enabled").Length == 0)
 18        {
 19            _adminEnabled = props.getIceProperty("Ice.Admin.Endpoints").Length > 0;
 20        }
 21        else
 22        {
 23            _adminEnabled = props.getIcePropertyAsInt("Ice.Admin.Enabled") > 0;
 24        }
 25
 26        if (_adminEnabled)
 27        {
 28            string[] facetFilter = props.getIcePropertyAsList("Ice.Admin.Facets");
 29            if (facetFilter.Length > 0)
 30            {
 31                _adminFacetFilter = new HashSet<string>(facetFilter);
 32            }
 33            else
 34            {
 35                _adminFacetFilter = new HashSet<string>();
 36            }
 37        }
 38
 39        _argv = args;
 40        _traceServiceObserver = _communicator.getProperties().getIcePropertyAsInt("IceBox.Trace.ServiceObserver");
 41    }
 42
 43    public void Dispose() => _sharedCommunicator?.Dispose();
 44
 45    public override void startService(string name, Ice.Current current)
 46    {
 47        var info = new ServiceInfo();
 48        lock (_mutex)
 49        {
 50            //
 51            // Search would be more efficient if services were contained in
 52            // a map, but order is required for shutdown.
 53            //
 54            int i;
 55            for (i = 0; i < _services.Count; ++i)
 56            {
 57                info = _services[i];
 58                if (info.name == name)
 59                {
 60                    if (_services[i].status != ServiceStatus.Stopped)
 61                    {
 62                        throw new AlreadyStartedException();
 63                    }
 64                    info.status = ServiceStatus.Starting;
 65                    _services[i] = info;
 66                    break;
 67                }
 68            }
 69            if (i == _services.Count)
 70            {
 71                throw new NoSuchServiceException();
 72            }
 73            _pendingStatusChanges = true;
 74        }
 75
 76        bool started = false;
 77        try
 78        {
 79            info.service.start(
 80                info.name,
 81                info.communicator ?? _sharedCommunicator,
 82                info.args);
 83            started = true;
 84        }
 85        catch (Exception e)
 86        {
 87            _logger.warning("ServiceManager: exception while starting service " + info.name + ":\n" + e.ToString());
 88        }
 89
 90        lock (_mutex)
 91        {
 92            int i;
 93            for (i = 0; i < _services.Count; ++i)
 94            {
 95                info = _services[i];
 96                if (info.name.Equals(name, StringComparison.Ordinal))
 97                {
 98                    if (started)
 99                    {
 100                        info.status = ServiceStatus.Started;
 101
 102                        var services = new List<string>
 103                        {
 104                            name
 105                        };
 106                        servicesStarted(services, _observers.Keys);
 107                    }
 108                    else
 109                    {
 110                        info.status = ServiceStatus.Stopped;
 111                    }
 112                    _services[i] = info;
 113                    break;
 114                }
 115            }
 116            _pendingStatusChanges = false;
 117            Monitor.PulseAll(_mutex);
 118        }
 119    }
 120
 121    public override void stopService(string name, Ice.Current current)
 122    {
 123        var info = new ServiceInfo();
 124        lock (_mutex)
 125        {
 126            //
 127            // Search would be more efficient if services were contained in
 128            // a map, but order is required for shutdown.
 129            //
 130            int i;
 131            for (i = 0; i < _services.Count; ++i)
 132            {
 133                info = _services[i];
 134                if (info.name.Equals(name, StringComparison.Ordinal))
 135                {
 136                    if (info.status != ServiceStatus.Started)
 137                    {
 138                        throw new AlreadyStoppedException();
 139                    }
 140                    info.status = ServiceStatus.Stopping;
 141                    _services[i] = info;
 142                    break;
 143                }
 144            }
 145            if (i == _services.Count)
 146            {
 147                throw new NoSuchServiceException();
 148            }
 149            _pendingStatusChanges = true;
 150        }
 151
 152        bool stopped = false;
 153        try
 154        {
 155            info.service.stop();
 156            stopped = true;
 157        }
 158        catch (Exception e)
 159        {
 160            _logger.warning("ServiceManager: exception while stopping service " + info.name + "\n" + e.ToString());
 161        }
 162
 163        lock (_mutex)
 164        {
 165            int i;
 166            for (i = 0; i < _services.Count; ++i)
 167            {
 168                info = _services[i];
 169                if (info.name == name)
 170                {
 171                    if (stopped)
 172                    {
 173                        info.status = ServiceStatus.Stopped;
 174
 175                        var services = new List<string>
 176                        {
 177                            name
 178                        };
 179                        servicesStopped(services, _observers.Keys);
 180                    }
 181                    else
 182                    {
 183                        info.status = ServiceStatus.Started;
 184                    }
 185                    _services[i] = info;
 186                    break;
 187                }
 188            }
 189            _pendingStatusChanges = false;
 190            Monitor.PulseAll(_mutex);
 191        }
 192    }
 193
 194    public override bool isServiceRunning(string name, Ice.Current current)
 195    {
 196        lock (_mutex)
 197        {
 198            foreach (ServiceInfo info in _services)
 199            {
 200                if (info.name == name)
 201                {
 202                    return info.status == ServiceStatus.Started;
 203                }
 204            }
 205        }
 206        throw new NoSuchServiceException();
 207    }
 208
 209    public override void addObserver(ServiceObserverPrx observer, Ice.Current current)
 210    {
 211        var activeServices = new List<string>();
 212
 213        //
 214        // Null observers and duplicate registrations are ignored
 215        //
 216        lock (_mutex)
 217        {
 218            if (observer != null)
 219            {
 220                try
 221                {
 222                    _observers.Add(observer, true);
 223                }
 224                catch (ArgumentException)
 225                {
 226                    return;
 227                }
 228
 229                if (_traceServiceObserver >= 1)
 230                {
 231                    _logger.trace(
 232                        "IceBox.ServiceObserver",
 233                        "Added service observer " + _communicator.proxyToString(observer));
 234                }
 235
 236                foreach (ServiceInfo info in _services)
 237                {
 238                    if (info.status == ServiceStatus.Started)
 239                    {
 240                        activeServices.Add(info.name);
 241                    }
 242                }
 243            }
 244        }
 245
 246        if (activeServices.Count > 0)
 247        {
 248            _ = servicesStartedAsync(observer, activeServices.ToArray());
 249        }
 250
 251        async Task servicesStartedAsync(ServiceObserverPrx observer, string[] services)
 252        {
 253            try
 254            {
 255                await observer.servicesStartedAsync(services).ConfigureAwait(false);
 256            }
 257            catch (System.Exception ex)
 258            {
 259                removeObserver(observer, ex);
 260            }
 261        }
 262    }
 263
 264    public override void shutdown(Ice.Current current) => _communicator.shutdown();
 265
 266    public int run()
 267    {
 268        try
 269        {
 270            Ice.Properties properties = _communicator.getProperties();
 271
 272            //
 273            // Parse the property set with the prefix "IceBox.Service.". These
 274            // properties should have the following format:
 275            //
 276            // IceBox.Service.Foo=<assembly>:Package.Foo [args]
 277            //
 278            // We parse the service properties specified in IceBox.LoadOrder
 279            // first, then the ones from remaining services.
 280            //
 281            string prefix = "IceBox.Service.";
 282            Dictionary<string, string> services = properties.getPropertiesForPrefix(prefix);
 283
 284            if (services.Count == 0)
 285            {
 286                throw new FailureException("ServiceManager: configuration must include at least one IceBox service.");
 287            }
 288
 289            string[] loadOrder = properties.getIcePropertyAsList("IceBox.LoadOrder");
 290            var servicesInfo = new List<StartServiceInfo>();
 291            for (int i = 0; i < loadOrder.Length; ++i)
 292            {
 293                if (loadOrder[i].Length > 0)
 294                {
 295                    string key = prefix + loadOrder[i];
 296                    if (!services.TryGetValue(key, out string value))
 297                    {
 298                        throw new FailureException($"ServiceManager: no service definition for '{loadOrder[i]}'.");
 299                    }
 300                    servicesInfo.Add(new StartServiceInfo(loadOrder[i], value, _argv));
 301                    services.Remove(key);
 302                }
 303            }
 304            foreach (KeyValuePair<string, string> entry in services)
 305            {
 306                string name = entry.Key[prefix.Length..];
 307                string value = entry.Value;
 308                servicesInfo.Add(new StartServiceInfo(name, value, _argv));
 309            }
 310
 311            //
 312            // Check if some services are using the shared communicator in which
 313            // case we create the shared communicator now with a property set that
 314            // is the union of all the service properties (from services that use
 315            // the shared communicator).
 316            //
 317            if (properties.getPropertiesForPrefix("IceBox.UseSharedCommunicator.").Count > 0)
 318            {
 319                var initData = new Ice.InitializationData();
 320                initData.properties = createServiceProperties("SharedCommunicator");
 321                foreach (StartServiceInfo service in servicesInfo)
 322                {
 323                    if (properties.getIcePropertyAsInt("IceBox.UseSharedCommunicator." + service.name) <= 0)
 324                    {
 325                        continue;
 326                    }
 327
 328                    //
 329                    // Load the service properties using the shared communicator properties as
 330                    // the default properties.
 331                    //
 332                    var svcProperties = new Ice.Properties(ref service.args, initData.properties);
 333
 334                    //
 335                    // Remove properties from the shared property set that a service explicitly clears.
 336                    //
 337                    Dictionary<string, string> allProps = initData.properties.getPropertiesForPrefix("");
 338                    foreach (string key in allProps.Keys)
 339                    {
 340                        if (svcProperties.getProperty(key).Length == 0)
 341                        {
 342                            initData.properties.setProperty(key, "");
 343                        }
 344                    }
 345
 346                    //
 347                    // Add the service properties to the shared communicator properties.
 348                    //
 349                    foreach (KeyValuePair<string, string> entry in svcProperties.getPropertiesForPrefix(""))
 350                    {
 351                        initData.properties.setProperty(entry.Key, entry.Value);
 352                    }
 353
 354                    //
 355                    // Parse <service>.* command line options (the Ice command line options
 356                    // were parsed by the Properties constructor called above).
 357                    //
 358                    service.args = initData.properties.parseCommandLineOptions(service.name, service.args);
 359                }
 360
 361                string facetNamePrefix = "IceBox.SharedCommunicator.";
 362                bool addFacets = configureAdmin(initData.properties, facetNamePrefix);
 363
 364                _sharedCommunicator = new Ice.Communicator(initData);
 365
 366                if (addFacets)
 367                {
 368                    // Add all facets created on shared communicator to the IceBox communicator
 369                    // but renamed <prefix>.<facet-name>, except for the Process facet which is
 370                    // never added.
 371                    foreach (KeyValuePair<string, Ice.Object> p in _sharedCommunicator.findAllAdminFacets())
 372                    {
 373                        if (p.Key != "Process")
 374                        {
 375                            _communicator.addAdminFacet(p.Value, facetNamePrefix + p.Key);
 376                        }
 377                    }
 378                }
 379            }
 380
 381            foreach (StartServiceInfo s in servicesInfo)
 382            {
 383                startService(s.name, s.entryPoint, s.args);
 384            }
 385
 386            //
 387            // Start Admin (if enabled).
 388            //
 389            _communicator.addAdminFacet(this, "IceBox.ServiceManager");
 390            _communicator.getAdmin();
 391
 392            //
 393            // We may want to notify external scripts that the services
 394            // have started and that IceBox is "ready".
 395            // This is done by defining the property IceBox.PrintServicesReady=bundleName
 396            //
 397            // bundleName is whatever you choose to call this set of
 398            // services. It will be echoed back as "bundleName ready".
 399            //
 400            // This must be done after start() has been invoked on the
 401            // services.
 402            //
 403            string bundleName = properties.getIceProperty("IceBox.PrintServicesReady");
 404            if (bundleName.Length > 0)
 405            {
 406                Console.Out.WriteLine(bundleName + " ready");
 407            }
 408
 409            _communicator.waitForShutdown();
 410        }
 411        catch (FailureException ex)
 412        {
 413            _logger.error(ex.ToString());
 414            return 1;
 415        }
 416        catch (Ice.CommunicatorDestroyedException)
 417        {
 418            // Expected if the communicator is destroyed
 419        }
 420        catch (Ice.ObjectAdapterDeactivatedException)
 421        {
 422            // Expected if the communicator is shutdown
 423        }
 424        catch (Ice.ObjectAdapterDestroyedException)
 425        {
 426            // Expected if the communicator is destroyed
 427        }
 428        catch (Exception ex)
 429        {
 430            _logger.error("ServiceManager: caught exception:\n" + ex.ToString());
 431            return 1;
 432        }
 433        finally
 434        {
 435            //
 436            // Invoke stop() on the services.
 437            //
 438            stopAll();
 439        }
 440
 441        return 0;
 442    }
 443
 444    private void startService(string service, string entryPoint, string[] args)
 445    {
 446        lock (_mutex)
 447        {
 448            //
 449            // Extract the assembly name and the class name.
 450            //
 451            string err = "ServiceManager: unable to load service '" + entryPoint + "': ";
 452            int sepPos = entryPoint.IndexOf(':', StringComparison.Ordinal);
 453            if (sepPos != -1)
 454            {
 455                const string driveLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 456                if (entryPoint.Length > 3 &&
 457                   sepPos == 1 &&
 458                   driveLetters.Contains(entryPoint[0], StringComparison.Ordinal) &&
 459                   (entryPoint[2] == '\\' || entryPoint[2] == '/'))
 460                {
 461                    sepPos = entryPoint.IndexOf(':', 3);
 462                }
 463            }
 464            if (sepPos == -1)
 465            {
 466                throw new FailureException($"{err}invalid entry point format.");
 467            }
 468
 469            System.Reflection.Assembly serviceAssembly = null;
 470            string assemblyName = entryPoint[..sepPos];
 471            string className = entryPoint[(sepPos + 1)..];
 472
 473            try
 474            {
 475                //
 476                // First try to load the assembly using Assembly.Load, which will succeed
 477                // if a fully-qualified name is provided or if a partial name has been qualified
 478                // in configuration. If that fails, try Assembly.LoadFrom(), which will succeed
 479                // if a file name is configured or a partial name is configured and DEVPATH is used.
 480                //
 481                try
 482                {
 483                    serviceAssembly = System.Reflection.Assembly.Load(assemblyName);
 484                }
 485                catch (Exception ex)
 486                {
 487                    try
 488                    {
 489                        serviceAssembly = System.Reflection.Assembly.LoadFrom(assemblyName);
 490                    }
 491                    catch (Exception)
 492                    {
 493                        throw ex;
 494                    }
 495                }
 496            }
 497            catch (Exception ex)
 498            {
 499                throw new FailureException($"{err}unable to load assembly: {assemblyName}", ex);
 500            }
 501
 502            //
 503            // Instantiate the class.
 504            //
 505            Type c = null;
 506            try
 507            {
 508                c = serviceAssembly.GetType(className, true);
 509            }
 510            catch (Exception ex)
 511            {
 512                throw new FailureException($"{err}GetType failed for '{className}'.", ex);
 513            }
 514
 515            var info = new ServiceInfo();
 516            info.name = service;
 517            info.status = ServiceStatus.Stopped;
 518            info.args = args;
 519
 520            //
 521            // If IceBox.UseSharedCommunicator.<name> is defined, create a
 522            // communicator for the service. The communicator inherits
 523            // from the shared communicator properties. If it's not
 524            // defined, add the service properties to the shared
 525            // communicator property set.
 526            //
 527            Ice.Communicator communicator;
 528            if (_communicator.getProperties().getIcePropertyAsInt("IceBox.UseSharedCommunicator." + service) > 0)
 529            {
 530                Debug.Assert(_sharedCommunicator != null);
 531                communicator = _sharedCommunicator;
 532            }
 533            else
 534            {
 535                //
 536                // Create the service properties. We use the communicator properties as the default
 537                // properties if IceBox.InheritProperties is set.
 538                //
 539                var initData = new Ice.InitializationData();
 540                initData.properties = createServiceProperties(service);
 541                if (info.args.Length > 0)
 542                {
 543                    //
 544                    // Create the service properties with the given service arguments. This should
 545                    // read the service config file if it's specified with --Ice.Config.
 546                    //
 547                    initData.properties = new Ice.Properties(ref info.args, initData.properties);
 548
 549                    //
 550                    // Next, parse the service "<service>.*" command line options (the Ice command
 551                    // line options were parsed by the Properties constructor above)
 552                    //
 553                    info.args = initData.properties.parseCommandLineOptions(service, info.args);
 554                }
 555
 556                //
 557                // If Admin is enabled on the IceBox communicator, for each service that does not set
 558                // Ice.Admin.Enabled, we set Ice.Admin.Enabled=1 to have this service create facets; then
 559                // we add these facets to the IceBox Admin object as IceBox.Service.<service>.<facet>.
 560                //
 561                string serviceFacetNamePrefix = "IceBox.Service." + service + ".";
 562                bool addFacets = configureAdmin(initData.properties, serviceFacetNamePrefix);
 563
 564                info.communicator = new Ice.Communicator(initData);
 565                communicator = info.communicator;
 566
 567                if (addFacets)
 568                {
 569                    // Add all facets created on the service communicator to the IceBox communicator
 570                    // but renamed IceBox.Service.<service>.<facet-name>, except for the Process facet
 571                    // which is never added
 572                    foreach (KeyValuePair<string, Ice.Object> p in communicator.findAllAdminFacets())
 573                    {
 574                        if (p.Key != "Process")
 575                        {
 576                            _communicator.addAdminFacet(p.Value, serviceFacetNamePrefix + p.Key);
 577                        }
 578                    }
 579                }
 580            }
 581
 582            try
 583            {
 584                //
 585                // Instantiate the service.
 586                //
 587                try
 588                {
 589                    //
 590                    // If the service class provides a constructor that accepts an Ice.Communicator argument,
 591                    // use that in preference to the default constructor.
 592                    //
 593                    var parameterTypes = new Type[1];
 594                    parameterTypes[0] = typeof(Ice.Communicator);
 595                    System.Reflection.ConstructorInfo ci = c.GetConstructor(parameterTypes);
 596                    if (ci != null)
 597                    {
 598                        try
 599                        {
 600                            object[] parameters = new object[1];
 601                            parameters[0] = _communicator;
 602                            info.service = (Service)ci.Invoke(parameters);
 603                        }
 604                        catch (MethodAccessException ex)
 605                        {
 606                            throw new FailureException(
 607                                $"{err}unable to access service constructor {className}(Ice.Communicator).",
 608                                ex);
 609                        }
 610                    }
 611                    else
 612                    {
 613                        //
 614                        // Fall back to the default constructor.
 615                        //
 616                        try
 617                        {
 618                            info.service = (Service)Ice.Internal.AssemblyUtil.createInstance(c) ??
 619                                throw new FailureException($"{err}no default constructor for '{className}'.");
 620                        }
 621                        catch (UnauthorizedAccessException ex)
 622                        {
 623                            throw new FailureException(
 624                                $"{err}unauthorized access to default service constructor for '{className}'.", ex);
 625                        }
 626                    }
 627                }
 628                catch (FailureException)
 629                {
 630                    throw;
 631                }
 632                catch (InvalidCastException ex)
 633                {
 634                    throw new FailureException($"{err}service does not implement IceBox.Service.", ex);
 635                }
 636                catch (System.Reflection.TargetInvocationException ex)
 637                {
 638                    if (ex.InnerException is FailureException)
 639                    {
 640                        throw ex.InnerException;
 641                    }
 642                    else
 643                    {
 644                        throw new FailureException(
 645                            $"{err}exception in service constructor for '{className}'.", ex.InnerException);
 646                    }
 647                }
 648                catch (Exception ex)
 649                {
 650                    throw new FailureException($"{err}exception in service constructor for '{className}'.", ex);
 651                }
 652
 653                try
 654                {
 655                    info.service.start(service, communicator, info.args);
 656                }
 657                catch (FailureException)
 658                {
 659                    throw;
 660                }
 661                catch (Exception ex)
 662                {
 663                    throw new FailureException($"{err}exception while starting service '{service}'.", ex);
 664                }
 665
 666                info.status = ServiceStatus.Started;
 667                _services.Add(info);
 668            }
 669            catch (Exception)
 670            {
 671                if (info.communicator != null)
 672                {
 673                    destroyServiceCommunicator(service, info.communicator);
 674                }
 675                throw;
 676            }
 677        }
 678    }
 679
 680    private void stopAll()
 681    {
 682        lock (_mutex)
 683        {
 684            //
 685            // First wait for any active startService/stopService calls to complete.
 686            //
 687            while (_pendingStatusChanges)
 688            {
 689                Monitor.Wait(_mutex);
 690            }
 691
 692            //
 693            // For each service, we call stop on the service.
 694            // Services are stopped in the reverse order of the order they were started.
 695            //
 696            _services.Reverse();
 697            var stoppedServices = new List<string>();
 698            foreach (ServiceInfo info in _services)
 699            {
 700                if (info.status == ServiceStatus.Started)
 701                {
 702                    try
 703                    {
 704                        info.service.stop();
 705                        stoppedServices.Add(info.name);
 706                    }
 707                    catch (Exception e)
 708                    {
 709                        _logger.warning(
 710                            "IceBox.ServiceManager: exception while stopping service " +
 711                                        info.name +
 712                                        ":\n" +
 713                                        e.ToString());
 714                    }
 715                }
 716
 717                if (info.communicator != null)
 718                {
 719                    destroyServiceCommunicator(info.name, info.communicator);
 720                }
 721            }
 722
 723            if (_sharedCommunicator != null)
 724            {
 725                removeAdminFacets("IceBox.SharedCommunicator.");
 726
 727                try
 728                {
 729                    _sharedCommunicator.destroy();
 730                }
 731                catch (Exception e)
 732                {
 733                    _logger.warning(
 734                        "ServiceManager: exception while destroying shared communicator:\n" + e.ToString());
 735                }
 736                _sharedCommunicator = null;
 737            }
 738
 739            _services.Clear();
 740            servicesStopped(stoppedServices, _observers.Keys);
 741        }
 742    }
 743
 744    private void servicesStarted(List<string> services, Dictionary<ServiceObserverPrx, bool>.KeyCollection observers)
 745    {
 746        if (services.Count > 0)
 747        {
 748            string[] servicesArray = services.ToArray();
 749
 750            foreach (ServiceObserverPrx observer in observers)
 751            {
 752                _ = servicesStartedAsync(observer, servicesArray);
 753            }
 754        }
 755
 756        async Task servicesStartedAsync(ServiceObserverPrx observer, string[] services)
 757        {
 758            try
 759            {
 760                await observer.servicesStartedAsync(services).ConfigureAwait(false);
 761            }
 762            catch (Ice.CommunicatorDestroyedException)
 763            {
 764                // Expected during shutdown if the observer's communicator is destroyed.
 765            }
 766            catch (System.Exception ex)
 767            {
 768                removeObserver(observer, ex);
 769            }
 770        }
 771    }
 772
 773    private void servicesStopped(List<string> services, Dictionary<ServiceObserverPrx, bool>.KeyCollection observers)
 774    {
 775        if (services.Count > 0)
 776        {
 777            string[] servicesArray = services.ToArray();
 778
 779            foreach (ServiceObserverPrx observer in observers)
 780            {
 781                _ = servicesStoppedAsync(observer, servicesArray);
 782            }
 783        }
 784
 785        async Task servicesStoppedAsync(ServiceObserverPrx observer, string[] services)
 786        {
 787            try
 788            {
 789                await observer.servicesStoppedAsync(services).ConfigureAwait(false);
 790            }
 791            catch (Ice.CommunicatorDestroyedException)
 792            {
 793                // Expected during shutdown if the observer's communicator is destroyed.
 794            }
 795            catch (System.Exception ex)
 796            {
 797                removeObserver(observer, ex);
 798            }
 799        }
 800    }
 801
 802    private void
 803    removeObserver(ServiceObserverPrx observer, System.Exception ex)
 804    {
 805        lock (_mutex)
 806        {
 807            if (_observers.Remove(observer))
 808            {
 809                observerRemoved(observer, ex);
 810            }
 811        }
 812    }
 813
 814    private void observerRemoved(ServiceObserverPrx observer, Exception ex)
 815    {
 816        if (_traceServiceObserver >= 1)
 817        {
 818            //
 819            // CommunicatorDestroyedException may occur during shutdown. The observer notification has
 820            // been sent, but the communicator was destroyed before the reply was received. We do not
 821            // log a message for this exception.
 822            //
 823            if (!(ex is Ice.CommunicatorDestroyedException))
 824            {
 825                _logger.trace(
 826                    "IceBox.ServiceObserver",
 827                    "Removed service observer " + _communicator.proxyToString(observer)
 828                    + "\nafter catching " + ex.ToString());
 829            }
 830        }
 831    }
 832
 833    private enum ServiceStatus
 834    {
 835        Stopping,
 836        Stopped,
 837        Starting,
 838        Started
 839    }
 840
 841    private struct ServiceInfo
 842    {
 843        public string name;
 844        public Service service;
 845        public Ice.Communicator communicator;
 846        public ServiceStatus status;
 847        public string[] args;
 848    }
 849
 850    private class StartServiceInfo
 851    {
 1852        public StartServiceInfo(string service, string value, string[] serverArgs)
 853        {
 854            //
 855            // Separate the entry point from the arguments.
 856            //
 1857            name = service;
 858
 859            try
 860            {
 1861                args = Ice.UtilInternal.Options.split(value);
 1862            }
 0863            catch (Ice.ParseException ex)
 864            {
 0865                throw new FailureException($"ServiceManager: invalid arguments for service '{name}'.", ex);
 866            }
 867
 868            Debug.Assert(args.Length > 0);
 869
 1870            entryPoint = args[0];
 871
 872            //
 873            // Shift the arguments.
 874            //
 1875            string[] tmp = new string[args.Length - 1];
 1876            Array.Copy(args, 1, tmp, 0, args.Length - 1);
 1877            args = tmp;
 878
 1879            if (serverArgs.Length > 0)
 880            {
 0881                var l = new ArrayList();
 0882                for (int j = 0; j < args.Length; j++)
 883                {
 0884                    l.Add(args[j]);
 885                }
 0886                for (int j = 0; j < serverArgs.Length; j++)
 887                {
 0888                    if (serverArgs[j].StartsWith("--" + service + ".", StringComparison.Ordinal))
 889                    {
 0890                        l.Add(serverArgs[j]);
 891                    }
 892                }
 0893                args = (string[])l.ToArray(typeof(string));
 894            }
 1895        }
 896
 897        public string name;
 898        public string entryPoint;
 899        public string[] args;
 900    }
 901
 902    private Ice.Properties createServiceProperties(string service)
 903    {
 904        var properties = new Ice.Properties();
 905        Ice.Properties communicatorProperties = _communicator.getProperties();
 906        if (communicatorProperties.getIcePropertyAsInt("IceBox.InheritProperties") > 0)
 907        {
 908            // Inherit all except IceBox. and Ice.Admin. properties
 909            foreach (KeyValuePair<string, string> property in communicatorProperties.getPropertiesForPrefix(""))
 910            {
 911                if (!property.Key.StartsWith("IceBox.", StringComparison.Ordinal) &&
 912                    !property.Key.StartsWith("Ice.Admin.", StringComparison.Ordinal))
 913                {
 914                    properties.setProperty(property.Key, property.Value);
 915                }
 916            }
 917        }
 918
 919        string programName = communicatorProperties.getIceProperty("Ice.ProgramName");
 920        if (programName.Length == 0)
 921        {
 922            properties.setProperty("Ice.ProgramName", service);
 923        }
 924        else
 925        {
 926            properties.setProperty("Ice.ProgramName", programName + "-" + service);
 927        }
 928        return properties;
 929    }
 930
 931    private void destroyServiceCommunicator(string service, Ice.Communicator communicator)
 932    {
 933        if (communicator != null)
 934        {
 935            try
 936            {
 937                communicator.shutdown();
 938                communicator.waitForShutdown();
 939            }
 940            catch (Ice.CommunicatorDestroyedException)
 941            {
 942                //
 943                // Ignore, the service might have already destroyed
 944                // the communicator for its own reasons.
 945                //
 946            }
 947            catch (Exception e)
 948            {
 949                _logger.warning("ServiceManager: exception while shutting down communicator for service "
 950                                + service + "\n" + e.ToString());
 951            }
 952
 953            removeAdminFacets("IceBox.Service." + service + ".");
 954            communicator.destroy();
 955        }
 956    }
 957
 958    private bool configureAdmin(Ice.Properties properties, string prefix)
 959    {
 960        if (_adminEnabled && properties.getIceProperty("Ice.Admin.Enabled").Length == 0)
 961        {
 962            var facetNames = new List<string>();
 963            foreach (string p in _adminFacetFilter)
 964            {
 965                if (p.StartsWith(prefix, StringComparison.Ordinal))
 966                {
 967                    facetNames.Add(p[prefix.Length..]);
 968                }
 969            }
 970
 971            if (_adminFacetFilter.Count == 0 || facetNames.Count > 0)
 972            {
 973                properties.setProperty("Ice.Admin.Enabled", "1");
 974
 975                if (facetNames.Count > 0)
 976                {
 977                    // TODO: need String.Join with escape!
 978                    properties.setProperty("Ice.Admin.Facets", string.Join(" ", facetNames.ToArray()));
 979                }
 980                return true;
 981            }
 982        }
 983        return false;
 984    }
 985
 986    private void removeAdminFacets(string prefix)
 987    {
 988        try
 989        {
 990            foreach (string p in _communicator.findAllAdminFacets().Keys)
 991            {
 992                if (p.StartsWith(prefix, StringComparison.Ordinal))
 993                {
 994                    _communicator.removeAdminFacet(p);
 995                }
 996            }
 997        }
 998        catch (Ice.CommunicatorDestroyedException)
 999        {
 1000            // Ignored
 1001        }
 1002        catch (Ice.ObjectAdapterDeactivatedException)
 1003        {
 1004            // Ignored
 1005        }
 1006        catch (Ice.ObjectAdapterDestroyedException)
 1007        {
 1008            // Ignored
 1009        }
 1010    }
 1011
 1012    private readonly Ice.Communicator _communicator;
 1013    private readonly bool _adminEnabled;
 1014    private readonly HashSet<string> _adminFacetFilter;
 1015    private Ice.Communicator _sharedCommunicator;
 1016
 1017#pragma warning disable CA2213
 1018    // This class does not own _logger.
 1019    private readonly Ice.Logger _logger;
 1020#pragma warning restore CA2213
 1021    private readonly string[] _argv; // Filtered server argument vector
 1022    private readonly List<ServiceInfo> _services = new();
 1023    private bool _pendingStatusChanges;
 1024    private readonly Dictionary<ServiceObserverPrx, bool> _observers = new();
 1025    private readonly int _traceServiceObserver;
 1026    private readonly object _mutex = new();
 1027}

Methods/Properties

.ctor(string, string, string[])