< Summary

Information
Class: Ice.ObjectAdapter.FailedDispatchPipeline
Assembly: Ice
File(s): /_/csharp/src/Ice/ObjectAdapter.cs
Tag: 125_37167941578
Line coverage
100%
Covered lines: 1
Uncovered lines: 0
Coverable lines: 1
Total lines: 1532
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 1
Fully covered methods: 1
Total methods: 1
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
dispatchAsync(...)100%11100%

File(s)

/_/csharp/src/Ice/ObjectAdapter.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3#nullable enable
 4
 5using Ice.Instrumentation;
 6using Ice.Internal;
 7using System.Diagnostics;
 8using System.Net;
 9using System.Net.Security;
 10using System.Text;
 11
 12namespace Ice;
 13
 14/// <summary>
 15/// An object adapter is the main server-side Ice API. It has two main purposes:
 16/// <list type="bullet">
 17/// <item>accept incoming connections from clients and dispatch requests received over these connections (see
 18/// <see cref="activate" />); and</item>
 19/// <item>maintain a dispatch pipeline and servants that handle the requests (see <see cref="add" />,
 20/// <see cref="addDefaultServant" />, and <see cref="use" />).</item>
 21/// </list>
 22/// An object adapter can dispatch "bidirectional requests"--requests it receives over an outgoing connection
 23/// instead of a more common incoming connection. It can also dispatch collocated requests (with no connection at
 24/// all).
 25/// </summary>
 26/// <seealso cref="Communicator.createObjectAdapter"/>
 27public sealed class ObjectAdapter
 28{
 29    private const int StateUninitialized = 0; // Just constructed.
 30    private const int StateHeld = 1;
 31    private const int StateActivating = 2;
 32    private const int StateActive = 3;
 33    private const int StateDeactivating = 4;
 34    private const int StateDeactivated = 5;
 35    private const int StateDestroying = 6;
 36    private const int StateDestroyed = 7;
 37
 38    private int _state = StateUninitialized;
 39    private readonly Instance _instance;
 40    private readonly Communicator _communicator;
 41    private ObjectAdapterFactory? _objectAdapterFactory;
 42    private Ice.Internal.ThreadPool? _threadPool;
 43    private readonly ServantManager _servantManager;
 44    private readonly string _name;
 45    private readonly string _id;
 46    private readonly string _replicaGroupId;
 47    private Reference? _reference;
 48    private readonly List<IncomingConnectionFactory> _incomingConnectionFactories;
 49    private RouterInfo? _routerInfo;
 50    private EndpointI[] _publishedEndpoints;
 51    private LocatorInfo? _locatorInfo;
 52    private int _directCount;  // The number of colloc proxies dispatching on this object adapter.
 53    private readonly bool _noConfig;
 54    private readonly int _messageSizeMax;
 55    private readonly SslServerAuthenticationOptions? _serverAuthenticationOptions;
 56
 57    private readonly Lazy<Object> _dispatchPipeline;
 58    private readonly Stack<Func<Object, Object>> _middlewareStack = new();
 59
 60    private readonly object _mutex = new();
 61
 62    /// <summary>
 63    /// Gets the name of this object adapter.
 64    /// </summary>
 65    /// <returns>This object adapter's name.</returns>
 66    public string getName() => _noConfig ? "" : _name;
 67
 68    /// <summary>
 69    /// Gets the communicator that created this object adapter.
 70    /// </summary>
 71    /// <returns>This object adapter's communicator.</returns>
 72    public Communicator getCommunicator() => _communicator;
 73
 74    /// <summary>
 75    /// Starts receiving and dispatching requests received over incoming connections.
 76    /// </summary>
 77    /// <remarks>When this object adapter is an indirect object adapter configured with a locator proxy, this
 78    /// method also registers the object adapter's published endpoints with this locator.</remarks>
 79    /// <seealso cref="deactivate"/>
 80    /// <seealso cref="getLocator"/>
 81    /// <seealso cref="getPublishedEndpoints"/>
 82    public void activate()
 83    {
 84        LocatorInfo? locatorInfo = null;
 85        bool printAdapterReady = false;
 86        bool hasPublishedEndpoints = false;
 87
 88        lock (_mutex)
 89        {
 90            checkForDeactivation();
 91
 92            //
 93            // If we've previously been initialized we just need to activate the
 94            // incoming connection factories and we're done.
 95            //
 96            if (_state != StateUninitialized)
 97            {
 98                foreach (IncomingConnectionFactory icf in _incomingConnectionFactories)
 99                {
 100                    icf.activate();
 101                }
 102                _state = StateActive;
 103                Monitor.PulseAll(_mutex);
 104                return;
 105            }
 106
 107            //
 108            // One off initializations of the adapter: update the
 109            // locator registry and print the "adapter ready"
 110            // message. We set state to StateActivating to prevent
 111            // deactivation from other threads while these one off
 112            // initializations are done.
 113            //
 114            _state = StateActivating;
 115
 116            locatorInfo = _locatorInfo;
 117            if (!_noConfig)
 118            {
 119                Properties properties = _instance.initializationData().properties!;
 120                printAdapterReady = properties.getIcePropertyAsInt("Ice.PrintAdapterReady") > 0;
 121            }
 122
 123            // We contact the locator registry once when this object adapter has one or more published endpoints.
 124            hasPublishedEndpoints = _publishedEndpoints.Length > 0;
 125        }
 126
 127        if (hasPublishedEndpoints)
 128        {
 129            try
 130            {
 131                var dummy = new Identity(name: "dummy", "");
 132                updateLocatorRegistry(locatorInfo, createDirectProxy(dummy));
 133            }
 134            catch (LocalException)
 135            {
 136                // If we couldn't update the locator registry, we let the exception go through and don't activate the
 137                // adapter to allow user code to retry activating the adapter later.
 138                lock (_mutex)
 139                {
 140                    _state = StateUninitialized;
 141                    Monitor.PulseAll(_mutex);
 142                }
 143                throw;
 144            }
 145        }
 146
 147        if (printAdapterReady)
 148        {
 149            Console.Out.WriteLine(_name + " ready");
 150        }
 151
 152        lock (_mutex)
 153        {
 154            Debug.Assert(_state == StateActivating);
 155
 156            foreach (IncomingConnectionFactory icf in _incomingConnectionFactories)
 157            {
 158                icf.activate();
 159            }
 160
 161            _state = StateActive;
 162            Monitor.PulseAll(_mutex);
 163        }
 164    }
 165
 166    /// <summary>
 167    /// Stops reading requests from incoming connections. Outstanding dispatches are not affected. The object
 168    /// adapter can be reactivated with <see cref="activate"/>.
 169    /// </summary>
 170    /// <remarks>This method is provided for backward compatibility with older versions of Ice. Don't use it in
 171    /// new applications. Holding is not immediate, i.e., after hold returns, the object adapter might still be
 172    /// active for some time. You can use <see cref="waitForHold"/> to wait until holding is complete.</remarks>
 173    public void hold()
 174    {
 175        lock (_mutex)
 176        {
 177            checkForDeactivation();
 178            _state = StateHeld;
 179            foreach (IncomingConnectionFactory factory in _incomingConnectionFactories)
 180            {
 181                factory.hold();
 182            }
 183        }
 184    }
 185
 186    /// <summary>
 187    /// Waits until the object adapter is in the holding state (see <see cref="hold"/>) and the dispatch of requests
 188    /// received over incoming connections has completed.
 189    /// </summary>
 190    /// <remarks>This method is provided for backward compatibility with older versions of Ice. Don't use it in
 191    /// new applications.</remarks>
 192    public void waitForHold()
 193    {
 194        List<IncomingConnectionFactory> incomingConnectionFactories;
 195        lock (_mutex)
 196        {
 197            checkForDeactivation();
 198            incomingConnectionFactories = new List<IncomingConnectionFactory>(_incomingConnectionFactories);
 199        }
 200
 201        foreach (IncomingConnectionFactory factory in incomingConnectionFactories)
 202        {
 203            factory.waitUntilHolding();
 204        }
 205    }
 206
 207    /// <summary>
 208    /// Deactivates this object adapter: stops accepting new connections from clients and closes gracefully all
 209    /// incoming connections created by this object adapter once all outstanding dispatches have completed. If this
 210    /// object adapter is indirect, this method also unregisters the object adapter from the locator
 211    /// (see <see cref="activate"/>).
 212    /// This method does not cancel outstanding dispatches: it lets them execute until completion.
 213    /// A deactivated object adapter cannot be reactivated again; it can only be destroyed.
 214    /// </summary>
 215    /// <seealso cref="waitForDeactivate"/>
 216    /// <seealso cref="Communicator.shutdown"/>
 217    public void deactivate()
 218    {
 219        bool hasPublishedEndpoints = false;
 220
 221        lock (_mutex)
 222        {
 223            // Wait for activation or a previous deactivation to complete.
 224            // This is necessary to avoid out of order locator updates.
 225            while (_state == StateActivating || _state == StateDeactivating)
 226            {
 227                Monitor.Wait(_mutex);
 228            }
 229            if (_state > StateDeactivating)
 230            {
 231                return;
 232            }
 233            _state = StateDeactivating;
 234
 235            hasPublishedEndpoints = _publishedEndpoints.Length > 0;
 236        }
 237
 238        // The locator infos and incoming connection factory list are immutable at this point.
 239
 240        if (hasPublishedEndpoints)
 241        {
 242            try
 243            {
 244                updateLocatorRegistry(_locatorInfo, null);
 245            }
 246            catch (LocalException)
 247            {
 248                // We can't throw exceptions in deactivate so we ignore failures to update the locator registry.
 249            }
 250        }
 251
 252        foreach (IncomingConnectionFactory factory in _incomingConnectionFactories)
 253        {
 254            factory.destroy();
 255        }
 256
 257        lock (_mutex)
 258        {
 259            Debug.Assert(_state == StateDeactivating);
 260            _state = StateDeactivated;
 261            Monitor.PulseAll(_mutex);
 262        }
 263    }
 264
 265    /// <summary>
 266    /// Waits until <see cref="deactivate"/> is called on this object adapter and all connections accepted by this
 267    /// object adapter are closed. A connection is closed only after all outstanding dispatches on this connection have
 268    /// completed.
 269    /// </summary>
 270    /// <seealso cref="Communicator.waitForShutdown"/>
 271    public void waitForDeactivate()
 272    {
 273        IncomingConnectionFactory[]? incomingConnectionFactories = null;
 274        lock (_mutex)
 275        {
 276            // Wait for deactivation of the adapter itself.
 277            while (_state < StateDeactivated)
 278            {
 279                Monitor.Wait(_mutex);
 280            }
 281            if (_state > StateDeactivated)
 282            {
 283                return;
 284            }
 285
 286            incomingConnectionFactories = _incomingConnectionFactories.ToArray();
 287        }
 288
 289        //
 290        // Now we wait for until all incoming connection factories are
 291        // finished.
 292        //
 293        foreach (IncomingConnectionFactory factory in incomingConnectionFactories)
 294        {
 295            factory.waitUntilFinished();
 296        }
 297    }
 298
 299    /// <summary>
 300    /// Checks whether or not <see cref="deactivate"/> was called on this object adapter.
 301    /// </summary>
 302    /// <returns><see langword="true"/> if <see cref="deactivate"/> was called on this object adapter; otherwise,
 303    /// <see langword="false"/>.</returns>
 304    public bool isDeactivated()
 305    {
 306        lock (_mutex)
 307        {
 308            return _state >= StateDeactivated;
 309        }
 310    }
 311
 312    /// <summary>
 313    /// Destroys this object adapter and cleans up all resources associated with it. Once this method has
 314    /// returned, you can recreate another object adapter with the same name.
 315    /// </summary>
 316    /// <seealso cref="Communicator.Dispose"/>
 317    public void destroy()
 318    {
 319        //
 320        // Deactivate and wait for completion.
 321        //
 322        deactivate();
 323        waitForDeactivate();
 324
 325        lock (_mutex)
 326        {
 327            //
 328            // Only a single thread is allowed to destroy the object
 329            // adapter. Other threads wait for the destruction to be
 330            // completed.
 331            //
 332            while (_state == StateDestroying)
 333            {
 334                Monitor.Wait(_mutex);
 335            }
 336            if (_state == StateDestroyed)
 337            {
 338                return;
 339            }
 340            _state = StateDestroying;
 341
 342            while (_directCount > 0)
 343            {
 344                Monitor.Wait(_mutex);
 345            }
 346        }
 347
 348        if (_routerInfo is not null)
 349        {
 350            // Remove entry from the router manager.
 351            _instance.routerManager().erase(_routerInfo.getRouter());
 352
 353            // Clear this object adapter with the router.
 354            _routerInfo.setAdapter(null);
 355        }
 356
 357        _instance.outgoingConnectionFactory().removeAdapter(this);
 358
 359        //
 360        // Now it's also time to clean up our servants and servant
 361        // locators.
 362        //
 363        _servantManager.destroy();
 364
 365        //
 366        // Destroy the thread pool.
 367        //
 368        if (_threadPool is not null)
 369        {
 370            _threadPool.destroy();
 371            _threadPool.joinWithAllThreads();
 372        }
 373
 374        _objectAdapterFactory?.removeObjectAdapter(this);
 375
 376        lock (_mutex)
 377        {
 378            //
 379            // We're done, now we can throw away all incoming connection
 380            // factories.
 381            //
 382            _incomingConnectionFactories.Clear();
 383
 384            //
 385            // Remove object references (some of them cyclic).
 386            //
 387            _threadPool = null;
 388            _routerInfo = null;
 389            _publishedEndpoints = [];
 390            _locatorInfo = null;
 391            _reference = null;
 392            _objectAdapterFactory = null;
 393
 394            _state = StateDestroyed;
 395            Monitor.PulseAll(_mutex);
 396        }
 397    }
 398
 399    /// <summary>
 400    /// Adds a middleware to the dispatch pipeline of this object adapter.
 401    /// </summary>
 402    /// <param name="middleware">The middleware factory that creates the new middleware when this object adapter
 403    /// creates its dispatch pipeline. A middleware factory is a function that takes an <see cref="Object"/> (the next
 404    /// element in the dispatch pipeline) and returns a new <see cref="Object"/> (the middleware you want to install in
 405    /// the pipeline).</param>
 406    /// <returns>This object adapter.</returns>
 407    /// <remarks>All middleware must be installed before the first dispatch. The middleware are executed in the order
 408    /// they are installed.</remarks>
 409    /// <exception cref="InvalidOperationException">Thrown when the object adapter's dispatch pipeline has already been
 410    /// created. This creation typically occurs the first time the object adapter dispatches an incoming request.
 411    /// </exception>
 412    public ObjectAdapter use(Func<Object, Object> middleware)
 413    {
 414        if (_dispatchPipeline.IsValueCreated)
 415        {
 416            throw new InvalidOperationException("All middleware must be installed before the first dispatch.");
 417        }
 418        _middlewareStack.Push(middleware);
 419        return this;
 420    }
 421
 422    /// <summary>
 423    /// Adds a servant to this object adapter's Active Servant Map (ASM).
 424    /// The ASM is a map {identity, facet} -> servant.
 425    /// </summary>
 426    /// <param name="servant">The servant to add.</param>
 427    /// <param name="id">The identity of the Ice object that is implemented by the servant.</param>
 428    /// <returns>A proxy that matches the given identity and this object adapter.</returns>
 429    /// <exception cref="AlreadyRegisteredException">Thrown when a servant with the same identity is already
 430    /// registered.</exception>
 431    /// <remarks>This method is equivalent to calling <see cref="addFacet"/> with an empty facet.</remarks>
 432    public ObjectPrx add(Object servant, Identity id) => addFacet(servant, id, "");
 433
 434    /// <summary>
 435    /// Adds a servant to this object adapter's Active Servant Map (ASM), while specifying a facet.
 436    /// The ASM is a map {identity, facet} -> servant.
 437    /// </summary>
 438    /// <param name="servant">The servant to add.</param>
 439    /// <param name="identity">The identity of the Ice object that is implemented by the servant.</param>
 440    /// <param name="facet">The facet of the Ice object that is implemented by the servant. An empty facet means the
 441    /// default facet.</param>
 442    /// <returns>A proxy that matches the given identity, facet, and this object adapter.</returns>
 443    /// <exception cref="AlreadyRegisteredException">Thrown when a servant with the same identity and facet is already
 444    /// registered.</exception>
 445    public ObjectPrx addFacet(Object servant, Identity identity, string facet)
 446    {
 447        lock (_mutex)
 448        {
 449            checkForDestruction();
 450            checkIdentity(identity);
 451            ArgumentNullException.ThrowIfNull(servant);
 452
 453            // Create a copy of the Identity argument, in case the caller reuses it.
 454            var id = new Identity(identity.name, identity.category);
 455            _servantManager.addServant(servant, id, facet);
 456
 457            return newProxy(id, facet);
 458        }
 459    }
 460
 461    /// <summary>
 462    /// Adds a servant to this object adapter's Active Servant Map (ASM), using an automatically generated UUID as its
 463    /// identity.
 464    /// </summary>
 465    /// <param name="servant">The servant to add.</param>
 466    /// <returns>A proxy with the generated UUID identity created by this object adapter.</returns>
 467    public ObjectPrx addWithUUID(Object servant) => addFacetWithUUID(servant, "");
 468
 469    /// <summary>
 470    /// Adds a servant to this object adapter's Active Servant Map (ASM), using an automatically generated UUID as
 471    /// its identity. Also specifies a facet.
 472    /// </summary>
 473    /// <param name="servant">The servant to add.</param>
 474    /// <param name="facet">The facet of the Ice object that is implemented by the servant. An empty facet means the
 475    /// default facet.</param>
 476    /// <returns>A proxy with the generated UUID identity and the specified facet.</returns>
 477    public ObjectPrx addFacetWithUUID(Object servant, string facet)
 478    {
 479        var ident = new Identity(Guid.NewGuid().ToString(), "");
 480        return addFacet(servant, ident, facet);
 481    }
 482
 483    /// <summary>
 484    /// Adds a default servant to handle requests for a specific category. When an object adapter dispatches an
 485    /// incoming request, it tries to find a servant for the identity and facet carried by the request in the
 486    /// following order:
 487    /// <list type="bullet">
 488    /// <item>The object adapter tries to find a servant for the identity and facet in the Active Servant Map.</item>
 489    /// <item>If this fails, the object adapter tries to find a default servant for the category component of the
 490    /// identity.</item>
 491    /// <item>If this fails, the object adapter tries to find a default servant for the empty category, regardless of
 492    /// the category contained in the identity.</item>
 493    /// <item>If this fails, the object adapter tries to find a servant locator for the category component of the
 494    /// identity. If there is no such servant locator, the object adapter tries to find a servant locator for the
 495    /// empty category.
 496    /// <list type="bullet">
 497    /// <item>If a servant locator is found, the object adapter tries to find a servant using this servant locator.
 498    /// </item>
 499    /// </list>
 500    /// </item>
 501    /// <item>If all the previous steps fail, the object adapter gives up and the caller receives an
 502    /// <see cref="ObjectNotExistException"/> or a <see cref="FacetNotExistException"/>.</item>
 503    /// </list>
 504    /// </summary>
 505    /// <param name="servant">The default servant to add.</param>
 506    /// <param name="category">The category for which the default servant is registered. The empty category means it
 507    /// handles all categories.</param>
 508    /// <exception cref="AlreadyRegisteredException">Thrown when a default servant with the same category is already
 509    /// registered.</exception>
 510    public void addDefaultServant(Ice.Object servant, string category)
 511    {
 512        ArgumentNullException.ThrowIfNull(servant);
 513
 514        lock (_mutex)
 515        {
 516            checkForDestruction();
 517            _servantManager.addDefaultServant(servant, category);
 518        }
 519    }
 520
 521    /// <summary>
 522    /// Removes a servant from the object adapter's Active Servant Map.
 523    /// </summary>
 524    /// <param name="id">The identity of the Ice object that is implemented by the servant.</param>
 525    /// <returns>The removed servant.</returns>
 526    /// <exception cref="NotRegisteredException">Thrown when no servant with the given identity is registered.
 527    /// </exception>
 528    public Object remove(Identity id) => removeFacet(id, "");
 529
 530    /// <summary>
 531    /// Removes a servant from the object adapter's Active Servant Map, while specifying a facet.
 532    /// </summary>
 533    /// <param name="id">The identity of the Ice object that is implemented by the servant.</param>
 534    /// <param name="facet">The facet. An empty facet means the default facet.</param>
 535    /// <returns>The removed servant.</returns>
 536    /// <exception cref="NotRegisteredException">Thrown when no servant with the given identity and facet is
 537    /// registered.</exception>
 538    public Object removeFacet(Identity id, string facet)
 539    {
 540        lock (_mutex)
 541        {
 542            checkForDestruction();
 543            checkIdentity(id);
 544
 545            return _servantManager.removeServant(id, facet);
 546        }
 547    }
 548
 549    /// <summary>
 550    /// Removes all facets with the given identity from the Active Servant Map. This method completely removes the
 551    /// Ice object, including its default facet.
 552    /// </summary>
 553    /// <param name="id">The identity of the Ice object to be removed.</param>
 554    /// <returns>A collection containing all the facet names and servants of the removed Ice object.</returns>
 555    /// <exception cref="NotRegisteredException">Thrown when no servant with the given identity is registered.
 556    /// </exception>
 557    public Dictionary<string, Object> removeAllFacets(Identity id)
 558    {
 559        lock (_mutex)
 560        {
 561            checkForDestruction();
 562            checkIdentity(id);
 563
 564            return _servantManager.removeAllFacets(id);
 565        }
 566    }
 567
 568    /// <summary>
 569    /// Removes the default servant for a specific category.
 570    /// </summary>
 571    /// <param name="category">The category of the default servant to remove.</param>
 572    /// <returns>The default servant.</returns>
 573    /// <exception cref="NotRegisteredException">Thrown when no default servant is registered for the given category.
 574    /// </exception>
 575    public Object removeDefaultServant(string category)
 576    {
 577        lock (_mutex)
 578        {
 579            checkForDestruction();
 580            return _servantManager.removeDefaultServant(category);
 581        }
 582    }
 583
 584    /// <summary>
 585    /// Looks up a servant.
 586    /// </summary>
 587    /// <param name="id">The identity of an Ice object.</param>
 588    /// <returns>The servant that implements the Ice object with the given identity, or null if no such servant has
 589    /// been found.</returns>
 590    /// <remarks>This method only tries to find the servant in the ASM and among the default servants. It does not
 591    /// attempt to locate a servant using servant locators.</remarks>
 592    public Object? find(Identity id) => findFacet(id, "");
 593
 594    /// <summary>
 595    /// Looks up a servant with an identity and facet.
 596    /// </summary>
 597    /// <param name="id">The identity of an Ice object.</param>
 598    /// <param name="facet">The facet of an Ice object. An empty facet means the default facet.</param>
 599    /// <returns>The servant that implements the Ice object with the given identity and facet,
 600    /// or null if no such servant has been found.</returns>
 601    /// <remarks>This method only tries to find the servant in the ASM and among the default servants. It does not
 602    /// attempt to locate a servant using servant locators.</remarks>
 603    public Object? findFacet(Identity id, string facet)
 604    {
 605        lock (_mutex)
 606        {
 607            checkForDestruction();
 608            checkIdentity(id);
 609
 610            return _servantManager.findServant(id, facet);
 611        }
 612    }
 613
 614    /// <summary>
 615    /// Finds all facets for a given identity in the Active Servant Map.
 616    /// </summary>
 617    /// <param name="id">The identity.</param>
 618    /// <returns>A collection containing all the facet names and servants that have been found. Can be empty.</returns>
 619    public Dictionary<string, Object> findAllFacets(Identity id)
 620    {
 621        lock (_mutex)
 622        {
 623            checkForDestruction();
 624            checkIdentity(id);
 625
 626            return _servantManager.findAllFacets(id);
 627        }
 628    }
 629
 630    /// <summary>
 631    /// Looks up a servant with an identity and a facet. It's equivalent to calling <see cref="findFacet"/>.
 632    /// </summary>
 633    /// <param name="proxy">The proxy that provides the identity and facet to search.</param>
 634    /// <returns>The servant that matches the identity and facet carried by <paramref name="proxy"/>, or null if no
 635    /// such servant has been found.</returns>
 636    /// <remarks>This method only tries to find the servant in the ASM and among the default servants. It does not
 637    /// attempt to locate a servant using servant locators.</remarks>
 638    public Object? findByProxy(ObjectPrx proxy)
 639    {
 640        lock (_mutex)
 641        {
 642            checkForDestruction();
 643            Reference @ref = ((ObjectPrxHelperBase)proxy).iceReference();
 644            return findFacet(@ref.getIdentity(), @ref.getFacet());
 645        }
 646    }
 647
 648    /// <summary>
 649    /// Adds a ServantLocator to this object adapter for a specific category.
 650    /// </summary>
 651    /// <param name="locator">The servant locator to add.</param>
 652    /// <param name="category">The category. The empty category means <paramref name="locator"/> handles all
 653    /// categories.</param>
 654    /// <exception cref="AlreadyRegisteredException">Thrown when a servant locator with the same category is already
 655    /// registered.</exception>
 656    /// <seealso cref="addDefaultServant"/>
 657    public void addServantLocator(ServantLocator locator, string category)
 658    {
 659        lock (_mutex)
 660        {
 661            checkForDestruction();
 662            _servantManager.addServantLocator(locator, category);
 663        }
 664    }
 665
 666    /// <summary>
 667    /// Removes a ServantLocator from this object adapter.
 668    /// </summary>
 669    /// <param name="category">The category.</param>
 670    /// <returns>The servant locator.</returns>
 671    /// <exception cref="NotRegisteredException">Thrown when no servant locator with the given category is registered.
 672    /// </exception>
 673    public ServantLocator removeServantLocator(string category)
 674    {
 675        lock (_mutex)
 676        {
 677            checkForDestruction();
 678            return _servantManager.removeServantLocator(category);
 679        }
 680    }
 681
 682    /// <summary>
 683    /// Finds a ServantLocator registered with this object adapter.
 684    /// </summary>
 685    /// <param name="category">The category.</param>
 686    /// <returns>The servant locator, or null if not found.</returns>
 687    public ServantLocator? findServantLocator(string category)
 688    {
 689        lock (_mutex)
 690        {
 691            checkForDestruction();
 692            return _servantManager.findServantLocator(category);
 693        }
 694    }
 695
 696    /// <summary>
 697    /// Finds the default servant for a specific category.
 698    /// </summary>
 699    /// <param name="category">The category.</param>
 700    /// <returns>The default servant, or null if not found.</returns>
 701    public Object? findDefaultServant(string category)
 702    {
 703        lock (_mutex)
 704        {
 705            checkForDestruction();
 706            return _servantManager.findDefaultServant(category);
 707        }
 708    }
 709
 710    /// <summary>
 711    /// Gets the dispatch pipeline of this object adapter.
 712    /// </summary>
 713    /// <value>The dispatch pipeline.</value>
 714    public Object dispatchPipeline => _dispatchPipeline.Value;
 715
 716    /// <summary>
 717    /// Creates a proxy from an Ice identity.
 718    /// If this object adapter is configured with an adapter ID, the proxy is an indirect proxy that refers to this
 719    /// adapter ID. If a replica group ID is also defined, the proxy is an indirect proxy that refers to this replica
 720    /// group ID. Otherwise, the proxy is a direct proxy containing this object adapter's published endpoints.
 721    /// </summary>
 722    /// <param name="id">An Ice identity.</param>
 723    /// <returns>A proxy with the given identity.</returns>
 724    public ObjectPrx createProxy(Identity id)
 725    {
 726        lock (_mutex)
 727        {
 728            checkForDestruction();
 729            checkIdentity(id);
 730
 731            return newProxy(id, "");
 732        }
 733    }
 734
 735    /// <summary>
 736    /// Creates a direct proxy from an Ice identity.
 737    /// </summary>
 738    /// <param name="id">An Ice identity.</param>
 739    /// <returns>A proxy with the given identity and the published endpoints of this object adapter.</returns>
 740    public ObjectPrx createDirectProxy(Identity id)
 741    {
 742        lock (_mutex)
 743        {
 744            checkForDestruction();
 745            checkIdentity(id);
 746
 747            return newDirectProxy(id, "");
 748        }
 749    }
 750
 751    /// <summary>
 752    /// Creates an indirect proxy for an Ice identity.
 753    /// </summary>
 754    /// <param name="id">An Ice identity.</param>
 755    /// <returns>An indirect proxy with the given identity. If this object adapter is not configured with an adapter
 756    /// ID or a replica group ID, the new proxy is a well-known proxy (i.e., an identity-only proxy).</returns>
 757    public ObjectPrx createIndirectProxy(Identity id)
 758    {
 759        lock (_mutex)
 760        {
 761            checkForDestruction();
 762            checkIdentity(id);
 763
 764            return newIndirectProxy(id, "", _id);
 765        }
 766    }
 767
 768    /// <summary>
 769    /// Sets an Ice locator on this object adapter.
 770    /// </summary>
 771    /// <param name="locator">The locator used by this object adapter.</param>
 772    public void setLocator(LocatorPrx? locator)
 773    {
 774        lock (_mutex)
 775        {
 776            checkForDeactivation();
 777            _locatorInfo = _instance.locatorManager().get(locator);
 778        }
 779    }
 780
 781    /// <summary>
 782    /// Gets the Ice locator used by this object adapter.
 783    /// </summary>
 784    /// <returns>The locator used by this object adapter, or null if no locator is used by this object adapter.
 785    /// </returns>
 786    public LocatorPrx? getLocator()
 787    {
 788        lock (_mutex)
 789        {
 790            return _locatorInfo?.getLocator();
 791        }
 792    }
 793
 794    /// <summary>
 795    /// Gets the set of endpoints configured on this object adapter.
 796    /// </summary>
 797    /// <returns>The set of endpoints.</returns>
 798    /// <remarks>This method remains usable after the object adapter has been deactivated.</remarks>
 799    public Endpoint[] getEndpoints()
 800    {
 801        lock (_mutex)
 802        {
 803            var endpoints = new List<Endpoint>();
 804            foreach (IncomingConnectionFactory factory in _incomingConnectionFactories)
 805            {
 806                endpoints.Add(factory.endpoint());
 807            }
 808            return endpoints.ToArray();
 809        }
 810    }
 811
 812    /// <summary>
 813    /// Gets the set of endpoints that proxies created by this object adapter will contain.
 814    /// </summary>
 815    /// <returns>The set of published endpoints.</returns>
 816    /// <remarks>This method remains usable after the object adapter has been deactivated.</remarks>
 817    public Endpoint[] getPublishedEndpoints()
 818    {
 819        lock (_mutex)
 820        {
 821            return (Endpoint[])_publishedEndpoints.Clone();
 822        }
 823    }
 824
 825    /// <summary>
 826    /// Sets the endpoints that proxies created by this object adapter will contain.
 827    /// </summary>
 828    /// <param name="newEndpoints">The new set of endpoints that the object adapter will embed in proxies.</param>
 829    /// <exception cref="ArgumentException">Thrown when <paramref name="newEndpoints"/> is empty or this adapter is
 830    /// associated with a router.</exception>
 831    public void setPublishedEndpoints(Endpoint[] newEndpoints)
 832    {
 833        if (newEndpoints.Length == 0)
 834        {
 835            throw new ArgumentException(
 836                $"The {nameof(newEndpoints)} argument must contain at least one endpoint.", nameof(newEndpoints));
 837        }
 838
 839        LocatorInfo? locatorInfo = null;
 840        EndpointI[] oldPublishedEndpoints;
 841
 842        lock (_mutex)
 843        {
 844            checkForDeactivation();
 845
 846            if (_routerInfo is not null)
 847            {
 848                throw new ArgumentException(
 849                    "Cannot set published endpoints on an object adapter associated with a router.");
 850            }
 851
 852            oldPublishedEndpoints = _publishedEndpoints;
 853            _publishedEndpoints = Array.ConvertAll(newEndpoints, endpt => (EndpointI)endpt);
 854            locatorInfo = _locatorInfo;
 855        }
 856
 857        try
 858        {
 859            var dummy = new Identity("dummy", "");
 860            updateLocatorRegistry(locatorInfo, createDirectProxy(dummy));
 861        }
 862        catch (LocalException)
 863        {
 864            lock (_mutex)
 865            {
 866                // Restore the old published endpoints.
 867                _publishedEndpoints = oldPublishedEndpoints;
 868                throw;
 869            }
 870        }
 871    }
 872
 873    internal bool isLocal(Reference r)
 874    {
 875        //
 876        // NOTE: it's important that isLocal() doesn't perform any blocking operations as
 877        // it can be called for AMI invocations if the proxy has no delegate set yet.
 878        //
 879
 880        if (r.isWellKnown())
 881        {
 882            // Check the active servant map to see if the well-known
 883            // proxy is for a local object.
 884            return _servantManager.hasServant(r.getIdentity());
 885        }
 886        else if (r.isIndirect())
 887        {
 888            // Proxy is local if the reference adapter id matches this
 889            // adapter id or replica group id.
 890            return r.getAdapterId().Equals(_id, StringComparison.Ordinal) ||
 891                r.getAdapterId().Equals(_replicaGroupId, StringComparison.Ordinal);
 892        }
 893        else
 894        {
 895            // Proxies which have at least one endpoint in common with the published endpoints are considered local.
 896            // This check doesn't take datagram endpoints into account; this effectively disables colloc optimization
 897            // for UDP.
 898            lock (_mutex)
 899            {
 900                checkForDestruction();
 901                IEnumerable<EndpointI> endpoints = r.getEndpoints().Where(e => !e.datagram());
 902                return _publishedEndpoints.Any(e => endpoints.Any(e.equivalent));
 903            }
 904        }
 905    }
 906
 907    internal void flushAsyncBatchRequests(Ice.CompressBatch compressBatch, CommunicatorFlushBatchAsync outAsync)
 908    {
 909        List<IncomingConnectionFactory> f;
 910        lock (_mutex)
 911        {
 912            f = new List<IncomingConnectionFactory>(_incomingConnectionFactories);
 913        }
 914
 915        foreach (IncomingConnectionFactory factory in f)
 916        {
 917            factory.flushAsyncBatchRequests(compressBatch, outAsync);
 918        }
 919    }
 920
 921    internal void updateConnectionObservers()
 922    {
 923        List<IncomingConnectionFactory> f;
 924        lock (_mutex)
 925        {
 926            f = new List<IncomingConnectionFactory>(_incomingConnectionFactories);
 927        }
 928
 929        foreach (IncomingConnectionFactory p in f)
 930        {
 931            p.updateConnectionObservers();
 932        }
 933    }
 934
 935    internal void updateThreadObservers()
 936    {
 937        Ice.Internal.ThreadPool? threadPool;
 938        lock (_mutex)
 939        {
 940            threadPool = _threadPool;
 941        }
 942
 943        threadPool?.updateObservers();
 944    }
 945
 946    internal void incDirectCount()
 947    {
 948        lock (_mutex)
 949        {
 950            checkForDestruction();
 951
 952            Debug.Assert(_directCount >= 0);
 953            ++_directCount;
 954        }
 955    }
 956
 957    internal void decDirectCount()
 958    {
 959        lock (_mutex)
 960        {
 961            // Not check for destruction here!
 962
 963            Debug.Assert(_instance is not null); // destroy waits for _directCount to reach 0
 964
 965            Debug.Assert(_directCount > 0);
 966            if (--_directCount == 0)
 967            {
 968                Monitor.PulseAll(_mutex);
 969            }
 970        }
 971    }
 972
 973    internal Ice.Internal.ThreadPool getThreadPool()
 974    {
 975        // No mutex lock necessary, _threadPool and _instance are
 976        // immutable after creation until they are removed in destroy().
 977
 978        // Not check for deactivation here!
 979
 980        Debug.Assert(_instance is not null); // Must not be called after destroy().
 981
 982        if (_threadPool is not null)
 983        {
 984            return _threadPool;
 985        }
 986        else
 987        {
 988            return _instance.serverThreadPool();
 989        }
 990    }
 991
 992    internal void setAdapterOnConnection(Ice.ConnectionI connection)
 993    {
 994        lock (_mutex)
 995        {
 996            checkForDestruction();
 997            connection.setAdapterFromAdapter(this);
 998        }
 999    }
 1000
 1001    internal int messageSizeMax() => _messageSizeMax;
 1002
 1003    //
 1004    // Only for use by ObjectAdapterFactory
 1005    //
 1006    internal ObjectAdapter(
 1007        Instance instance,
 1008        Communicator communicator,
 1009        ObjectAdapterFactory objectAdapterFactory,
 1010        string name,
 1011        RouterPrx? router,
 1012        bool noConfig,
 1013        SslServerAuthenticationOptions? serverAuthenticationOptions)
 1014    {
 1015        _instance = instance;
 1016        _communicator = communicator;
 1017        _objectAdapterFactory = objectAdapterFactory;
 1018        _servantManager = new ServantManager(instance, name);
 1019
 1020        _dispatchPipeline = new Lazy<Object>(createDispatchPipeline);
 1021        _name = name;
 1022        _incomingConnectionFactories = [];
 1023        _publishedEndpoints = [];
 1024        _routerInfo = null;
 1025        _directCount = 0;
 1026        _noConfig = noConfig;
 1027        _serverAuthenticationOptions = serverAuthenticationOptions;
 1028
 1029        // Install default middleware depending on the communicator's configuration.
 1030        if (_instance.initializationData().logger is Logger logger)
 1031        {
 1032            int warningLevel = _instance.initializationData().properties!.getIcePropertyAsInt("Ice.Warn.Dispatch");
 1033            if (_instance.traceLevels().dispatch > 0 || warningLevel > 0)
 1034            {
 1035                use(next =>
 1036                    new LoggerMiddleware(
 1037                        next,
 1038                        logger,
 1039                        _instance.traceLevels().dispatch,
 1040                        _instance.traceLevels().dispatchCat,
 1041                        warningLevel,
 1042                        _instance.toStringMode()));
 1043            }
 1044        }
 1045        if (_instance.initializationData().observer is CommunicatorObserver observer)
 1046        {
 1047            use(next => new ObserverMiddleware(next, observer));
 1048        }
 1049
 1050        if (_noConfig)
 1051        {
 1052            _id = "";
 1053            _replicaGroupId = "";
 1054            _reference = _instance.referenceFactory().create("dummy -t", "");
 1055            return;
 1056        }
 1057
 1058        Properties properties = _instance.initializationData().properties!;
 1059        Properties.validatePropertiesWithPrefix(_name, properties, PropertyNames.ObjectAdapterProps);
 1060
 1061        // Make sure named adapter has configuration.
 1062        if (router is null && properties.getPropertiesForPrefix($"{_name}.").Count == 0)
 1063        {
 1064            // These need to be set to prevent warnings/asserts in the destructor.
 1065            _state = StateDestroyed;
 1066            _incomingConnectionFactories = [];
 1067
 1068            throw new InitializationException($"Object adapter '{name}' requires configuration.");
 1069        }
 1070
 1071        _id = properties.getProperty(_name + ".AdapterId");
 1072        _replicaGroupId = properties.getProperty(_name + ".ReplicaGroupId");
 1073
 1074        // Setup a reference to be used to get the default proxy options
 1075        // when creating new proxies. By default, create twoway proxies.
 1076        string proxyOptions = properties.getPropertyWithDefault(_name + ".ProxyOptions", "-t");
 1077        try
 1078        {
 1079            _reference = _instance.referenceFactory().create("dummy " + proxyOptions, "");
 1080        }
 1081        catch (ParseException ex)
 1082        {
 1083            throw new InitializationException(
 1084                $"Invalid proxy options '{proxyOptions}' for object adapter '{_name}'.",
 1085                ex);
 1086        }
 1087
 1088        // The maximum size of an Ice protocol message in bytes. This is limited to 0x7fffffff, which corresponds to
 1089        // the maximum value of a 32-bit signed integer (int).
 1090        const int messageSizeMaxUpperLimit = int.MaxValue;
 1091        int defaultMessageSizeMax = instance.messageSizeMax() / 1024;
 1092        int messageSizeMax = properties.getPropertyAsIntWithDefault($"{_name}.MessageSizeMax", defaultMessageSizeMax);
 1093        if (messageSizeMax > messageSizeMaxUpperLimit / 1024)
 1094        {
 1095            throw new Ice.InitializationException(
 1096                $"{_name}.MessageSizeMax '{messageSizeMax}' is too large, it must be less than or equal to '{messageSize
 1097        }
 1098        else if (messageSizeMax < 1)
 1099        {
 1100            _messageSizeMax = messageSizeMaxUpperLimit;
 1101        }
 1102        else
 1103        {
 1104            // The property is specified in kibibytes (KiB); _messageSizeMax is stored in bytes.
 1105            _messageSizeMax = messageSizeMax * 1024;
 1106        }
 1107
 1108        try
 1109        {
 1110            // If the user configured any of the ObjectAdapter.ThreadPool properties, create a per-adapter thread pool.
 1111            // Otherwise the OA will use the default server thread pool.
 1112            if (properties.getPropertiesForPrefix(_name + ".ThreadPool.").Count > 0)
 1113            {
 1114                _threadPool = new Ice.Internal.ThreadPool(_instance, _name + ".ThreadPool", 0);
 1115            }
 1116
 1117            router ??= RouterPrxHelper.uncheckedCast(communicator.propertyToProxy(_name + ".Router"));
 1118            if (router is not null)
 1119            {
 1120                _routerInfo = _instance.routerManager().get(router);
 1121                Debug.Assert(_routerInfo is not null);
 1122
 1123                if (properties.getProperty($"{_name}.Endpoints").Length > 0)
 1124                {
 1125                    throw new InitializationException(
 1126                        "An object adapter with a router cannot accept incoming connections.");
 1127                }
 1128
 1129                // Make sure this router is not already registered with another adapter.
 1130                if (_routerInfo.getAdapter() is not null)
 1131                {
 1132                    throw new AlreadyRegisteredException(
 1133                        "object adapter with router",
 1134                        Util.identityToString(router.ice_getIdentity(), _instance.toStringMode()));
 1135                }
 1136
 1137                // Associate this object adapter with the router. This way,
 1138                // new outgoing connections to the router's client proxy will
 1139                // use this object adapter for callbacks.
 1140                _routerInfo.setAdapter(this);
 1141
 1142                // Also modify all existing outgoing connections to the
 1143                // router's client proxy to use this object adapter for callbacks.
 1144                _instance.outgoingConnectionFactory().setRouterInfo(_routerInfo);
 1145            }
 1146            else
 1147            {
 1148                // Parse the endpoints, but don't store them in the adapter. The connection
 1149                // factory might change it, for example, to fill in the real port number.
 1150                List<EndpointI> endpoints = parseEndpoints(properties.getProperty(_name + ".Endpoints"), true);
 1151
 1152                if (endpoints.Count == 0)
 1153                {
 1154                    TraceLevels tl = _instance.traceLevels();
 1155                    if (tl.network >= 2)
 1156                    {
 1157                        _instance.initializationData().logger!.trace(
 1158                            tl.networkCat,
 1159                            $"created adapter '{_name}' without endpoints");
 1160                    }
 1161                }
 1162                else
 1163                {
 1164                    // An adapter with endpoints accepts incoming connections, and each connection needs the
 1165                    // adapter's dispatch thread pool. Create it before the first connection factory: its
 1166                    // on-demand creation reads the thread pool properties and can throw, and it must not fail
 1167                    // once connections exist.
 1168                    getThreadPool();
 1169
 1170                    foreach (EndpointI endp in endpoints)
 1171                    {
 1172                        foreach (EndpointI expanded in endp.expandHost())
 1173                        {
 1174                            var factory = new IncomingConnectionFactory(instance, expanded, this);
 1175                            _incomingConnectionFactories.Add(factory);
 1176                        }
 1177                    }
 1178                }
 1179            }
 1180
 1181            // Parse published endpoints.
 1182            _publishedEndpoints = computePublishedEndpoints();
 1183
 1184            if (properties.getProperty(_name + ".Locator").Length > 0)
 1185            {
 1186                setLocator(LocatorPrxHelper.uncheckedCast(communicator.propertyToProxy(_name + ".Locator")));
 1187            }
 1188            else
 1189            {
 1190                setLocator(_instance.referenceFactory().getDefaultLocator());
 1191            }
 1192        }
 1193        catch (LocalException)
 1194        {
 1195            destroy();
 1196            throw;
 1197        }
 1198    }
 1199
 1200    internal static void checkIdentity(Identity ident)
 1201    {
 1202        if (ident.name.Length == 0)
 1203        {
 1204            throw new ArgumentException("The name of an Ice object identity cannot be empty.", nameof(ident));
 1205        }
 1206    }
 1207
 1208    internal SslServerAuthenticationOptions? getServerAuthenticationOptions() => _serverAuthenticationOptions;
 1209
 1210    private ObjectPrx newProxy(Identity ident, string facet)
 1211    {
 1212        if (_id.Length == 0)
 1213        {
 1214            return newDirectProxy(ident, facet);
 1215        }
 1216        else if (_replicaGroupId.Length == 0)
 1217        {
 1218            return newIndirectProxy(ident, facet, _id);
 1219        }
 1220        else
 1221        {
 1222            return newIndirectProxy(ident, facet, _replicaGroupId);
 1223        }
 1224    }
 1225
 1226    private ObjectPrx newDirectProxy(Identity ident, string facet) =>
 1227        new ObjectPrxHelper(_instance.referenceFactory().create(ident, facet, _reference, _publishedEndpoints));
 1228
 1229    private ObjectPrx newIndirectProxy(Identity ident, string facet, string id) =>
 1230        new ObjectPrxHelper(_instance.referenceFactory().create(ident, facet, _reference, id));
 1231
 1232    private void checkForDeactivation()
 1233    {
 1234        checkForDestruction();
 1235
 1236        if (_state >= StateDeactivating)
 1237        {
 1238            throw new ObjectAdapterDeactivatedException(getName());
 1239        }
 1240    }
 1241
 1242    private void checkForDestruction()
 1243    {
 1244        if (_state >= StateDestroying)
 1245        {
 1246            throw new ObjectAdapterDestroyedException(getName());
 1247        }
 1248    }
 1249
 1250    private List<EndpointI> parseEndpoints(string endpts, bool oaEndpoints)
 1251    {
 1252        int beg;
 1253        int end = 0;
 1254
 1255        string delim = " \t\n\r";
 1256
 1257        var endpoints = new List<EndpointI>();
 1258        while (end < endpts.Length)
 1259        {
 1260            beg = Ice.UtilInternal.StringUtil.findFirstNotOf(endpts, delim, end);
 1261            if (beg == -1)
 1262            {
 1263                if (endpoints.Count != 0)
 1264                {
 1265                    throw new ParseException("invalid empty object adapter endpoint");
 1266                }
 1267                break;
 1268            }
 1269
 1270            end = beg;
 1271            while (true)
 1272            {
 1273                end = endpts.IndexOf(':', end);
 1274                if (end == -1)
 1275                {
 1276                    end = endpts.Length;
 1277                    break;
 1278                }
 1279                else
 1280                {
 1281                    bool quoted = false;
 1282                    int quote = beg;
 1283                    while (true)
 1284                    {
 1285                        quote = endpts.IndexOf('\"', quote);
 1286                        if (quote == -1 || end < quote)
 1287                        {
 1288                            break;
 1289                        }
 1290                        else
 1291                        {
 1292                            quote = endpts.IndexOf('\"', ++quote);
 1293                            if (quote == -1)
 1294                            {
 1295                                break;
 1296                            }
 1297                            else if (end < quote)
 1298                            {
 1299                                quoted = true;
 1300                                break;
 1301                            }
 1302                            ++quote;
 1303                        }
 1304                    }
 1305                    if (!quoted)
 1306                    {
 1307                        break;
 1308                    }
 1309                    ++end;
 1310                }
 1311            }
 1312
 1313            if (end == beg)
 1314            {
 1315                throw new ParseException("invalid empty object adapter endpoint");
 1316            }
 1317
 1318            string s = endpts[beg..end];
 1319            EndpointI endp = _instance.endpointFactoryManager().create(s, oaEndpoints) ??
 1320                throw new ParseException($"invalid object adapter endpoint '{s}'");
 1321            endpoints.Add(endp);
 1322
 1323            ++end;
 1324        }
 1325
 1326        return endpoints;
 1327    }
 1328
 1329    private EndpointI[] computePublishedEndpoints()
 1330    {
 1331        IEnumerable<EndpointI> endpoints;
 1332        if (_routerInfo is not null)
 1333        {
 1334            // Get the router's server proxy endpoints and use them as the published endpoints.
 1335            endpoints = _routerInfo.getServerEndpoints();
 1336        }
 1337        else
 1338        {
 1339            // Parse published endpoints. If set, these are used instead of the connection factory endpoints.
 1340            endpoints = parseEndpoints(
 1341                _instance.initializationData().properties!.getProperty($"{_name}.PublishedEndpoints"),
 1342                oaEndpoints: false);
 1343
 1344            if (!endpoints.Any())
 1345            {
 1346                // If the PublishedEndpoints property isn't set, we compute the published endpoints from the factory
 1347                // endpoints.
 1348                endpoints = _incomingConnectionFactories.Select(f => f.endpoint());
 1349
 1350                // Remove all loopback/multicast endpoints.
 1351                IEnumerable<EndpointI> endpointsNoLoopback = endpoints.Where(e => !e.isLoopbackOrMulticast());
 1352
 1353                // Retrieve published host
 1354                string publishedHost = _instance.initializationData().properties!.getProperty($"{_name}.PublishedHost");
 1355
 1356                if (endpointsNoLoopback.Any())
 1357                {
 1358                    endpoints = endpointsNoLoopback;
 1359
 1360                    // For non-loopback & non-multicast endpoints, we use the fully qualified name of the local host as
 1361                    // default for publishedHost.
 1362                    if (publishedHost.Length == 0)
 1363                    {
 1364                        publishedHost = Dns.GetHostEntry("").HostName; // fully qualified name of local host
 1365                    }
 1366                }
 1367
 1368                // Replace the host in all endpoints by publishedHost (when applicable) and clear all local options.
 1369                endpoints = endpoints.Select(e => e.toPublishedEndpoint(publishedHost)).Distinct();
 1370            }
 1371        }
 1372
 1373        EndpointI[] endpointsArray = endpoints.ToArray();
 1374
 1375        if (_instance.traceLevels().network >= 1 && endpointsArray.Length > 0)
 1376        {
 1377            var s = new StringBuilder("published endpoints for object adapter '");
 1378            s.Append(_name);
 1379            s.Append("':\n");
 1380            bool first = true;
 1381            foreach (EndpointI endpoint in endpointsArray)
 1382            {
 1383                if (!first)
 1384                {
 1385                    s.Append(':');
 1386                }
 1387                s.Append(endpoint.ToString());
 1388                first = false;
 1389            }
 1390            _instance.initializationData().logger!.trace(_instance.traceLevels().networkCat, s.ToString());
 1391        }
 1392
 1393        return endpointsArray;
 1394    }
 1395
 1396    private void updateLocatorRegistry(LocatorInfo? locatorInfo, ObjectPrx? proxy)
 1397    {
 1398        if (_id.Length == 0 || locatorInfo is null)
 1399        {
 1400            return; // Nothing to update.
 1401        }
 1402
 1403        // Call on the locator registry outside the synchronization to
 1404        // blocking other threads that need to lock this OA.
 1405        LocatorRegistryPrx locatorRegistry = locatorInfo.getLocatorRegistry();
 1406        if (locatorRegistry is null)
 1407        {
 1408            return;
 1409        }
 1410
 1411        try
 1412        {
 1413            if (_replicaGroupId.Length == 0)
 1414            {
 1415                locatorRegistry.setAdapterDirectProxy(_id, proxy);
 1416            }
 1417            else
 1418            {
 1419                locatorRegistry.setReplicatedAdapterDirectProxy(_id, _replicaGroupId, proxy);
 1420            }
 1421        }
 1422        catch (AdapterNotFoundException)
 1423        {
 1424            if (_instance!.traceLevels().location >= 1)
 1425            {
 1426                var s = new StringBuilder();
 1427                s.Append("couldn't update object adapter `" + _id + "' endpoints with the locator registry:\n");
 1428                s.Append("the object adapter is not known to the locator registry");
 1429                _instance.initializationData().logger!.trace(_instance.traceLevels().locationCat, s.ToString());
 1430            }
 1431
 1432            throw new NotRegisteredException("object adapter", _id);
 1433        }
 1434        catch (InvalidReplicaGroupIdException)
 1435        {
 1436            if (_instance.traceLevels().location >= 1)
 1437            {
 1438                var s = new StringBuilder();
 1439                s.Append("couldn't update object adapter `" + _id + "' endpoints with the locator registry:\n");
 1440                s.Append("the replica group `" + _replicaGroupId + "' is not known to the locator registry");
 1441                _instance.initializationData().logger!.trace(_instance.traceLevels().locationCat, s.ToString());
 1442            }
 1443
 1444            throw new NotRegisteredException("replica group", _replicaGroupId);
 1445        }
 1446        catch (AdapterAlreadyActiveException)
 1447        {
 1448            if (_instance.traceLevels().location >= 1)
 1449            {
 1450                var s = new StringBuilder();
 1451                s.Append("couldn't update object adapter `" + _id + "' endpoints with the locator registry:\n");
 1452                s.Append("the object adapter endpoints are already set");
 1453                _instance.initializationData().logger!.trace(_instance.traceLevels().locationCat, s.ToString());
 1454            }
 1455
 1456            throw new ObjectAdapterIdInUseException(_id);
 1457        }
 1458        catch (ObjectAdapterDeactivatedException)
 1459        {
 1460            // Expected if collocated call and OA is deactivated, ignore.
 1461        }
 1462        catch (ObjectAdapterDestroyedException)
 1463        {
 1464            // Ignore
 1465        }
 1466        catch (CommunicatorDestroyedException)
 1467        {
 1468            // Ignore
 1469        }
 1470        catch (LocalException e)
 1471        {
 1472            if (_instance.traceLevels().location >= 1)
 1473            {
 1474                var s = new StringBuilder();
 1475                s.Append("couldn't update object adapter `" + _id + "' endpoints with the locator registry:\n");
 1476                s.Append(e.ToString());
 1477                _instance.initializationData().logger!.trace(_instance.traceLevels().locationCat, s.ToString());
 1478            }
 1479            throw; // TODO: Shall we raise a special exception instead of a non obvious local exception?
 1480        }
 1481
 1482        if (_instance.traceLevels().location >= 1)
 1483        {
 1484            var s = new StringBuilder();
 1485            s.Append("updated object adapter `" + _id + "' endpoints with the locator registry\n");
 1486            s.Append("endpoints = ");
 1487            if (proxy is not null)
 1488            {
 1489                Endpoint[] endpoints = proxy.ice_getEndpoints();
 1490                for (int i = 0; i < endpoints.Length; i++)
 1491                {
 1492                    s.Append(endpoints[i].ToString());
 1493                    if (i + 1 < endpoints.Length)
 1494                    {
 1495                        s.Append(':');
 1496                    }
 1497                }
 1498            }
 1499            _instance.initializationData().logger!.trace(_instance.traceLevels().locationCat, s.ToString());
 1500        }
 1501    }
 1502
 1503    private Object createDispatchPipeline()
 1504    {
 1505        try
 1506        {
 1507            Object dispatchPipeline = _servantManager; // the "final" dispatcher
 1508            foreach (Func<Object, Object> middleware in _middlewareStack)
 1509            {
 1510                dispatchPipeline = middleware(dispatchPipeline);
 1511            }
 1512            return dispatchPipeline;
 1513        }
 1514        catch (System.Exception ex)
 1515        {
 1516            _instance.initializationData().logger!.error(
 1517                $"failed to create the dispatch pipeline of object adapter '{_name}':\n{ex}");
 1518            return new FailedDispatchPipeline();
 1519        }
 1520        finally
 1521        {
 1522            _middlewareStack.Clear(); // we no longer need these functions
 1523        }
 1524    }
 1525
 1526    // Installed as the dispatch pipeline when the creation of the actual pipeline fails.
 1527    private class FailedDispatchPipeline : Ice.Object
 1528    {
 1529        public ValueTask<OutgoingResponse> dispatchAsync(IncomingRequest request) =>
 11530            throw new UnknownException("The object adapter could not create its dispatch pipeline.");
 1531    }
 1532}