< Summary

Information
Class: IceBox.ServiceManagerI.StartServiceInfo
Assembly: iceboxnet
File(s): /home/runner/work/ice/ice/csharp/src/iceboxnet/ServiceManagerI.cs
Tag: 71_18251537082
Line coverage
52%
Covered lines: 10
Uncovered lines: 9
Coverable lines: 19
Total lines: 1021
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)

/home/runner/work/ice/ice/csharp/src/iceboxnet/ServiceManagerI.cs

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

Methods/Properties

.ctor(string, string, string[])