< Summary

Information
Class: IceBox.ServiceManagerI.StartServiceInfo
Assembly: iceboxnet
File(s): /_/csharp/src/iceboxnet/ServiceManagerI.cs
Tag: 91_21789722663
Line coverage
52%
Covered lines: 10
Uncovered lines: 9
Coverable lines: 19
Total lines: 1030
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                            if (info.service == null)
 620                            {
 621                                throw new FailureException($"{err}no default constructor for '{className}'.");
 622                            }
 623                        }
 624                        catch (UnauthorizedAccessException ex)
 625                        {
 626                            throw new FailureException(
 627                                $"{err}unauthorized access to default service constructor for '{className}'.", ex);
 628                        }
 629                    }
 630                }
 631                catch (FailureException)
 632                {
 633                    throw;
 634                }
 635                catch (InvalidCastException ex)
 636                {
 637                    throw new FailureException($"{err}service does not implement IceBox.Service.", ex);
 638                }
 639                catch (System.Reflection.TargetInvocationException ex)
 640                {
 641                    if (ex.InnerException is FailureException)
 642                    {
 643                        throw ex.InnerException;
 644                    }
 645                    else
 646                    {
 647                        throw new FailureException(
 648                            $"{err}exception in service constructor for '{className}'.", ex.InnerException);
 649                    }
 650                }
 651                catch (Exception ex)
 652                {
 653                    throw new FailureException($"{err}exception in service constructor for '{className}'.", ex);
 654                }
 655
 656                try
 657                {
 658                    info.service.start(service, communicator, info.args);
 659                }
 660                catch (FailureException)
 661                {
 662                    throw;
 663                }
 664                catch (Exception ex)
 665                {
 666                    throw new FailureException($"{err}exception while starting service '{service}'.", ex);
 667                }
 668
 669                info.status = ServiceStatus.Started;
 670                _services.Add(info);
 671            }
 672            catch (Exception)
 673            {
 674                if (info.communicator != null)
 675                {
 676                    destroyServiceCommunicator(service, info.communicator);
 677                }
 678                throw;
 679            }
 680        }
 681    }
 682
 683    private void stopAll()
 684    {
 685        lock (_mutex)
 686        {
 687            //
 688            // First wait for any active startService/stopService calls to complete.
 689            //
 690            while (_pendingStatusChanges)
 691            {
 692                Monitor.Wait(_mutex);
 693            }
 694
 695            //
 696            // For each service, we call stop on the service.
 697            // Services are stopped in the reverse order of the order they were started.
 698            //
 699            _services.Reverse();
 700            var stoppedServices = new List<string>();
 701            foreach (ServiceInfo info in _services)
 702            {
 703                if (info.status == ServiceStatus.Started)
 704                {
 705                    try
 706                    {
 707                        info.service.stop();
 708                        stoppedServices.Add(info.name);
 709                    }
 710                    catch (Exception e)
 711                    {
 712                        _logger.warning(
 713                            "IceBox.ServiceManager: exception while stopping service " +
 714                                        info.name +
 715                                        ":\n" +
 716                                        e.ToString());
 717                    }
 718                }
 719
 720                if (info.communicator != null)
 721                {
 722                    destroyServiceCommunicator(info.name, info.communicator);
 723                }
 724            }
 725
 726            if (_sharedCommunicator != null)
 727            {
 728                removeAdminFacets("IceBox.SharedCommunicator.");
 729
 730                try
 731                {
 732                    _sharedCommunicator.destroy();
 733                }
 734                catch (Exception e)
 735                {
 736                    _logger.warning(
 737                        "ServiceManager: exception while destroying shared communicator:\n" + e.ToString());
 738                }
 739                _sharedCommunicator = null;
 740            }
 741
 742            _services.Clear();
 743            servicesStopped(stoppedServices, _observers.Keys);
 744        }
 745    }
 746
 747    private void servicesStarted(List<string> services, Dictionary<ServiceObserverPrx, bool>.KeyCollection observers)
 748    {
 749        //
 750        // Must be called with 'this' unlocked
 751        //
 752
 753        if (services.Count > 0)
 754        {
 755            string[] servicesArray = services.ToArray();
 756
 757            foreach (ServiceObserverPrx observer in observers)
 758            {
 759                _ = servicesStartedAsync(observer, servicesArray);
 760            }
 761        }
 762
 763        async Task servicesStartedAsync(ServiceObserverPrx observer, string[] services)
 764        {
 765            try
 766            {
 767                await observer.servicesStartedAsync(services).ConfigureAwait(false);
 768            }
 769            catch (System.Exception ex)
 770            {
 771                removeObserver(observer, ex);
 772            }
 773        }
 774    }
 775
 776    private void servicesStopped(List<string> services, Dictionary<ServiceObserverPrx, bool>.KeyCollection observers)
 777    {
 778        //
 779        // Must be called with 'this' unlocked
 780        //
 781
 782        if (services.Count > 0)
 783        {
 784            string[] servicesArray = services.ToArray();
 785
 786            foreach (ServiceObserverPrx observer in observers)
 787            {
 788                _ = servicesStoppedAsync(observer, servicesArray);
 789            }
 790        }
 791
 792        async Task servicesStoppedAsync(ServiceObserverPrx observer, string[] services)
 793        {
 794            try
 795            {
 796                await observer.servicesStoppedAsync(services).ConfigureAwait(false);
 797            }
 798            catch (System.Exception ex)
 799            {
 800                removeObserver(observer, ex);
 801            }
 802        }
 803    }
 804
 805    private void
 806    removeObserver(ServiceObserverPrx observer, System.Exception ex)
 807    {
 808        lock (_mutex)
 809        {
 810            if (_observers.Remove(observer))
 811            {
 812                observerRemoved(observer, ex);
 813            }
 814        }
 815    }
 816
 817    private void observerRemoved(ServiceObserverPrx observer, Exception ex)
 818    {
 819        if (_traceServiceObserver >= 1)
 820        {
 821            //
 822            // CommunicatorDestroyedException may occur during shutdown. The observer notification has
 823            // been sent, but the communicator was destroyed before the reply was received. We do not
 824            // log a message for this exception.
 825            //
 826            if (!(ex is Ice.CommunicatorDestroyedException))
 827            {
 828                _logger.trace(
 829                    "IceBox.ServiceObserver",
 830                    "Removed service observer " + _communicator.proxyToString(observer)
 831                    + "\nafter catching " + ex.ToString());
 832            }
 833        }
 834    }
 835
 836    private enum ServiceStatus
 837    {
 838        Stopping,
 839        Stopped,
 840        Starting,
 841        Started
 842    }
 843
 844    private struct ServiceInfo
 845    {
 846        public string name;
 847        public Service service;
 848        public Ice.Communicator communicator;
 849        public ServiceStatus status;
 850        public string[] args;
 851    }
 852
 853    private class StartServiceInfo
 854    {
 1855        public StartServiceInfo(string service, string value, string[] serverArgs)
 856        {
 857            //
 858            // Separate the entry point from the arguments.
 859            //
 1860            name = service;
 861
 862            try
 863            {
 1864                args = Ice.UtilInternal.Options.split(value);
 1865            }
 0866            catch (Ice.ParseException ex)
 867            {
 0868                throw new FailureException($"ServiceManager: invalid arguments for service '{name}'.", ex);
 869            }
 870
 871            Debug.Assert(args.Length > 0);
 872
 1873            entryPoint = args[0];
 874
 875            //
 876            // Shift the arguments.
 877            //
 1878            string[] tmp = new string[args.Length - 1];
 1879            Array.Copy(args, 1, tmp, 0, args.Length - 1);
 1880            args = tmp;
 881
 1882            if (serverArgs.Length > 0)
 883            {
 0884                var l = new ArrayList();
 0885                for (int j = 0; j < args.Length; j++)
 886                {
 0887                    l.Add(args[j]);
 888                }
 0889                for (int j = 0; j < serverArgs.Length; j++)
 890                {
 0891                    if (serverArgs[j].StartsWith("--" + service + ".", StringComparison.Ordinal))
 892                    {
 0893                        l.Add(serverArgs[j]);
 894                    }
 895                }
 0896                args = (string[])l.ToArray(typeof(string));
 897            }
 1898        }
 899
 900        public string name;
 901        public string entryPoint;
 902        public string[] args;
 903    }
 904
 905    private Ice.Properties createServiceProperties(string service)
 906    {
 907        var properties = new Ice.Properties();
 908        Ice.Properties communicatorProperties = _communicator.getProperties();
 909        if (communicatorProperties.getIcePropertyAsInt("IceBox.InheritProperties") > 0)
 910        {
 911            // Inherit all except IceBox. and Ice.Admin. properties
 912            foreach (KeyValuePair<string, string> property in communicatorProperties.getPropertiesForPrefix(""))
 913            {
 914                if (!property.Key.StartsWith("IceBox.", StringComparison.Ordinal) &&
 915                    !property.Key.StartsWith("Ice.Admin.", StringComparison.Ordinal))
 916                {
 917                    properties.setProperty(property.Key, property.Value);
 918                }
 919            }
 920        }
 921
 922        string programName = communicatorProperties.getIceProperty("Ice.ProgramName");
 923        if (programName.Length == 0)
 924        {
 925            properties.setProperty("Ice.ProgramName", service);
 926        }
 927        else
 928        {
 929            properties.setProperty("Ice.ProgramName", programName + "-" + service);
 930        }
 931        return properties;
 932    }
 933
 934    private void destroyServiceCommunicator(string service, Ice.Communicator communicator)
 935    {
 936        if (communicator != null)
 937        {
 938            try
 939            {
 940                communicator.shutdown();
 941                communicator.waitForShutdown();
 942            }
 943            catch (Ice.CommunicatorDestroyedException)
 944            {
 945                //
 946                // Ignore, the service might have already destroyed
 947                // the communicator for its own reasons.
 948                //
 949            }
 950            catch (Exception e)
 951            {
 952                _logger.warning("ServiceManager: exception while shutting down communicator for service "
 953                                + service + "\n" + e.ToString());
 954            }
 955
 956            removeAdminFacets("IceBox.Service." + service + ".");
 957            communicator.destroy();
 958        }
 959    }
 960
 961    private bool configureAdmin(Ice.Properties properties, string prefix)
 962    {
 963        if (_adminEnabled && properties.getIceProperty("Ice.Admin.Enabled").Length == 0)
 964        {
 965            var facetNames = new List<string>();
 966            foreach (string p in _adminFacetFilter)
 967            {
 968                if (p.StartsWith(prefix, StringComparison.Ordinal))
 969                {
 970                    facetNames.Add(p[prefix.Length..]);
 971                }
 972            }
 973
 974            if (_adminFacetFilter.Count == 0 || facetNames.Count > 0)
 975            {
 976                properties.setProperty("Ice.Admin.Enabled", "1");
 977
 978                if (facetNames.Count > 0)
 979                {
 980                    // TODO: need String.Join with escape!
 981                    properties.setProperty("Ice.Admin.Facets", string.Join(" ", facetNames.ToArray()));
 982                }
 983                return true;
 984            }
 985        }
 986        return false;
 987    }
 988
 989    private void removeAdminFacets(string prefix)
 990    {
 991        try
 992        {
 993            foreach (string p in _communicator.findAllAdminFacets().Keys)
 994            {
 995                if (p.StartsWith(prefix, StringComparison.Ordinal))
 996                {
 997                    _communicator.removeAdminFacet(p);
 998                }
 999            }
 1000        }
 1001        catch (Ice.CommunicatorDestroyedException)
 1002        {
 1003            // Ignored
 1004        }
 1005        catch (Ice.ObjectAdapterDeactivatedException)
 1006        {
 1007            // Ignored
 1008        }
 1009        catch (Ice.ObjectAdapterDestroyedException)
 1010        {
 1011            // Ignored
 1012        }
 1013    }
 1014
 1015    private readonly Ice.Communicator _communicator;
 1016    private readonly bool _adminEnabled;
 1017    private readonly HashSet<string> _adminFacetFilter;
 1018    private Ice.Communicator _sharedCommunicator;
 1019
 1020#pragma warning disable CA2213
 1021    // This class does not own _logger.
 1022    private readonly Ice.Logger _logger;
 1023#pragma warning restore CA2213
 1024    private readonly string[] _argv; // Filtered server argument vector
 1025    private readonly List<ServiceInfo> _services = new();
 1026    private bool _pendingStatusChanges;
 1027    private readonly Dictionary<ServiceObserverPrx, bool> _observers = new();
 1028    private readonly int _traceServiceObserver;
 1029    private readonly object _mutex = new();
 1030}

Methods/Properties

.ctor(string, string, string[])