< Summary

Information
Class: Ice.Internal.RoutableReference.LocatorEndpointsCallback
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/Internal/Reference.cs
Tag: 71_18251537082
Line coverage
100%
Covered lines: 11
Uncovered lines: 0
Coverable lines: 11
Total lines: 1277
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 3
Total methods: 3
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
setEndpoints(...)100%22100%
setException(...)100%11100%

File(s)

/home/runner/work/ice/ice/csharp/src/Ice/Internal/Reference.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Diagnostics;
 4using System.Globalization;
 5using System.Text;
 6
 7namespace Ice.Internal;
 8
 9public abstract class Reference : IEquatable<Reference>
 10{
 11    /// <summary>
 12    /// Represents the invocation mode of a proxy.
 13    /// </summary>
 14    public enum Mode
 15    {
 16        /// <summary>
 17        /// A two-way invocation that always expects a response.
 18        /// </summary>
 19        ModeTwoway,
 20
 21        /// <summary>
 22        /// A one-way invocation that does not expect a response and completes as soon as the request is sent.
 23        /// </summary>
 24        ModeOneway,
 25
 26        /// <summary>
 27        /// A batch one-way invocation that allows grouping several one-way invocations into a single batch.
 28        /// </summary>
 29        ModeBatchOneway,
 30
 31        /// <summary>
 32        /// A datagram invocation that does not expect a response and is sent using UDP.
 33        /// </summary>
 34        ModeDatagram,
 35
 36        /// <summary>
 37        /// A batch datagram invocation that allows grouping several datagram invocations into a single batch.
 38        /// </summary>
 39        ModeBatchDatagram
 40    }
 41
 42    public interface GetConnectionCallback
 43    {
 44        void setConnection(Ice.ConnectionI connection, bool compress);
 45
 46        void setException(Ice.LocalException ex);
 47    }
 48
 49    internal abstract BatchRequestQueue batchRequestQueue { get; }
 50
 51    internal bool isBatch => _mode is Mode.ModeBatchOneway or Mode.ModeBatchDatagram;
 52
 53    internal bool isTwoway => _mode is Mode.ModeTwoway;
 54
 55    public Mode getMode() => _mode;
 56
 57    public Ice.ProtocolVersion getProtocol() => _protocol;
 58
 59    public Ice.EncodingVersion getEncoding() => _encoding;
 60
 61    public Ice.Identity getIdentity() => _identity;
 62
 63    public string getFacet() => _facet;
 64
 65    public Instance getInstance() => _instance;
 66
 67    public Dictionary<string, string> getContext() => _context;
 68
 69    public TimeSpan
 70    getInvocationTimeout() => _invocationTimeout;
 71
 72    public bool?
 73    getCompress() => _compress;
 74
 75    public Ice.Communicator getCommunicator() => _communicator;
 76
 77    public abstract EndpointI[] getEndpoints();
 78
 79    public abstract string getAdapterId();
 80
 81    public abstract LocatorInfo getLocatorInfo();
 82
 83    public abstract RouterInfo getRouterInfo();
 84
 85    public abstract bool getCollocationOptimized();
 86
 87    public abstract bool getCacheConnection();
 88
 89    public abstract Ice.EndpointSelectionType getEndpointSelection();
 90
 91    public abstract TimeSpan getLocatorCacheTimeout();
 92
 93    public abstract string getConnectionId();
 94
 95    public abstract ThreadPool getThreadPool();
 96
 97    public abstract Connection getConnection();
 98
 99    //
 100    // The change* methods (here and in derived classes) create
 101    // a new reference based on the existing one, with the
 102    // corresponding value changed.
 103    //
 104    public Reference changeContext(Dictionary<string, string> newContext)
 105    {
 106        newContext ??= _emptyContext;
 107        Reference r = _instance.referenceFactory().copy(this);
 108        if (newContext.Count == 0)
 109        {
 110            r._context = _emptyContext;
 111        }
 112        else
 113        {
 114            r._context = new Dictionary<string, string>(newContext);
 115        }
 116        return r;
 117    }
 118
 119    public virtual Reference changeMode(Mode newMode)
 120    {
 121        Reference r = _instance.referenceFactory().copy(this);
 122        r._mode = newMode;
 123        return r;
 124    }
 125
 126    public Reference changeIdentity(Ice.Identity newIdentity)
 127    {
 128        Reference r = _instance.referenceFactory().copy(this);
 129        // Identity is a reference type, therefore we make a copy of newIdentity.
 130        r._identity = newIdentity with { };
 131        return r;
 132    }
 133
 134    public Reference changeFacet(string newFacet)
 135    {
 136        Reference r = _instance.referenceFactory().copy(this);
 137        r._facet = newFacet;
 138        return r;
 139    }
 140
 141    public Reference changeInvocationTimeout(TimeSpan newTimeout)
 142    {
 143        Reference r = _instance.referenceFactory().copy(this);
 144        r._invocationTimeout = newTimeout;
 145        return r;
 146    }
 147
 148    public virtual Reference changeEncoding(Ice.EncodingVersion newEncoding)
 149    {
 150        Reference r = _instance.referenceFactory().copy(this);
 151        r._encoding = newEncoding;
 152        return r;
 153    }
 154
 155    public virtual Reference changeCompress(bool newCompress)
 156    {
 157        Reference r = _instance.referenceFactory().copy(this);
 158        r._compress = newCompress;
 159        return r;
 160    }
 161
 162    public abstract Reference changeEndpoints(EndpointI[] newEndpoints);
 163
 164    public abstract Reference changeAdapterId(string newAdapterId);
 165
 166    public abstract Reference changeLocator(Ice.LocatorPrx newLocator);
 167
 168    public abstract Reference changeRouter(Ice.RouterPrx newRouter);
 169
 170    public abstract Reference changeCollocationOptimized(bool newCollocationOptimized);
 171
 172    public abstract Reference changeCacheConnection(bool newCache);
 173
 174    public abstract Reference changeEndpointSelection(Ice.EndpointSelectionType newType);
 175
 176    public abstract Reference changeLocatorCacheTimeout(TimeSpan newTimeout);
 177
 178    public abstract Reference changeConnectionId(string connectionId);
 179
 180    public abstract Reference changeConnection(Ice.ConnectionI connection);
 181
 182    // Gets the effective compression setting, taking into account the override.
 183    public bool? getCompressOverride()
 184    {
 185        DefaultsAndOverrides defaultsAndOverrides = getInstance().defaultsAndOverrides();
 186        return defaultsAndOverrides.overrideCompress ?? _compress;
 187    }
 188
 189    public abstract bool isIndirect();
 190
 191    public abstract bool isWellKnown();
 192
 193    //
 194    // Marshal the reference.
 195    //
 196    public virtual void streamWrite(Ice.OutputStream s)
 197    {
 198        //
 199        // Don't write the identity here. Operations calling streamWrite
 200        // write the identity.
 201        //
 202
 203        //
 204        // For compatibility with the old FacetPath.
 205        //
 206        if (_facet.Length == 0)
 207        {
 208            s.writeStringSeq(null);
 209        }
 210        else
 211        {
 212            string[] facetPath = { _facet };
 213            s.writeStringSeq(facetPath);
 214        }
 215
 216        s.writeByte((byte)_mode);
 217
 218        s.writeBool(false); // the secure field is no longer used
 219
 220        if (!s.getEncoding().Equals(Ice.Util.Encoding_1_0))
 221        {
 222            ProtocolVersion.ice_write(s, _protocol);
 223            EncodingVersion.ice_write(s, _encoding);
 224        }
 225
 226        // Derived class writes the remainder of the reference.
 227    }
 228
 229    //
 230    // Convert the reference to its string form.
 231    //
 232    public override string ToString()
 233    {
 234        //
 235        // WARNING: Certain features, such as proxy validation in Glacier2,
 236        // depend on the format of proxy strings. Changes to toString() and
 237        // methods called to generate parts of the reference string could break
 238        // these features. Please review for all features that depend on the
 239        // format of proxyToString() before changing this and related code.
 240        //
 241        var s = new StringBuilder();
 242
 243        Ice.ToStringMode toStringMode = _instance.toStringMode();
 244
 245        //
 246        // If the encoded identity string contains characters which
 247        // the reference parser uses as separators, then we enclose
 248        // the identity string in quotes.
 249        //
 250        string id = Ice.Util.identityToString(_identity, toStringMode);
 251        if (Ice.UtilInternal.StringUtil.findFirstOf(id, " :@") != -1)
 252        {
 253            s.Append('"');
 254            s.Append(id);
 255            s.Append('"');
 256        }
 257        else
 258        {
 259            s.Append(id);
 260        }
 261
 262        if (_facet.Length > 0)
 263        {
 264            //
 265            // If the encoded facet string contains characters which
 266            // the reference parser uses as separators, then we enclose
 267            // the facet string in quotes.
 268            //
 269            s.Append(" -f ");
 270            string fs = Ice.UtilInternal.StringUtil.escapeString(_facet, "", toStringMode);
 271            if (Ice.UtilInternal.StringUtil.findFirstOf(fs, " :@") != -1)
 272            {
 273                s.Append('"');
 274                s.Append(fs);
 275                s.Append('"');
 276            }
 277            else
 278            {
 279                s.Append(fs);
 280            }
 281        }
 282
 283        switch (_mode)
 284        {
 285            case Mode.ModeTwoway:
 286            {
 287                // Don't print the default mode.
 288                break;
 289            }
 290
 291            case Mode.ModeOneway:
 292            {
 293                s.Append(" -o");
 294                break;
 295            }
 296
 297            case Mode.ModeBatchOneway:
 298            {
 299                s.Append(" -O");
 300                break;
 301            }
 302
 303            case Mode.ModeDatagram:
 304            {
 305                s.Append(" -d");
 306                break;
 307            }
 308
 309            case Mode.ModeBatchDatagram:
 310            {
 311                s.Append(" -D");
 312                break;
 313            }
 314        }
 315
 316        if (_protocol != Ice.Util.Protocol_1_0)
 317        {
 318            // We print the protocol unless it's 1.0.
 319            s.Append(" -p ");
 320            s.Append(Ice.Util.protocolVersionToString(_protocol));
 321        }
 322
 323        // We print the encoding if it's not 1.1 or if Ice.Default.EncodingVersion is set to something other than 1.1.
 324        if (_encoding != Ice.Util.Encoding_1_1 ||
 325            _instance.defaultsAndOverrides().defaultEncoding != Ice.Util.Encoding_1_1)
 326        {
 327            s.Append(" -e ");
 328            s.Append(Ice.Util.encodingVersionToString(_encoding));
 329        }
 330
 331        return s.ToString();
 332
 333        // Derived class writes the remainder of the string.
 334    }
 335
 336    public abstract Dictionary<string, string> toProperty(string prefix);
 337
 338    internal abstract RequestHandler getRequestHandler();
 339
 340    public static bool operator ==(Reference lhs, Reference rhs) => lhs is null ? rhs is null : lhs.Equals(rhs);
 341
 342    public static bool operator !=(Reference lhs, Reference rhs) => !(lhs == rhs);
 343
 344    public override int GetHashCode()
 345    {
 346        var hash = new HashCode();
 347        hash.Add(_mode);
 348        hash.Add(_identity);
 349        hash.Add(_context.Count); // we only hash the count, not the contents
 350        hash.Add(_facet);
 351        hash.Add(_compress);
 352        // We don't hash protocol and encoding; they are usually "1.0" and "1.1" respectively.
 353        hash.Add(_invocationTimeout);
 354        return hash.ToHashCode();
 355    }
 356
 357    public virtual bool Equals(Reference other)
 358    {
 359        // The derived class checks ReferenceEquals and guarantees other is not null.
 360        Debug.Assert(other is not null);
 361
 362        return _mode == other._mode &&
 363            _identity == other._identity &&
 364            _context.DictionaryEqual(other._context) &&
 365            _facet == other._facet &&
 366            _compress == other._compress &&
 367            _protocol == other._protocol &&
 368            _encoding == other._encoding &&
 369            _invocationTimeout == other._invocationTimeout;
 370    }
 371
 372    public override bool Equals(object obj) => Equals(obj as Reference);
 373
 374    public virtual Reference Clone() => (Reference)MemberwiseClone();
 375
 376    private static readonly Dictionary<string, string> _emptyContext = new Dictionary<string, string>();
 377
 378    private protected Instance _instance;
 379    private readonly Ice.Communicator _communicator;
 380
 381    private Mode _mode;
 382    private Ice.Identity _identity;
 383    private Dictionary<string, string> _context;
 384    private string _facet;
 385    private Ice.ProtocolVersion _protocol;
 386    private Ice.EncodingVersion _encoding;
 387    private TimeSpan _invocationTimeout;
 388    private bool? _compress;
 389
 390    protected Reference(
 391        Instance instance,
 392        Ice.Communicator communicator,
 393        Ice.Identity identity,
 394        string facet,
 395        Mode mode,
 396        bool? compress,
 397        Ice.ProtocolVersion protocol,
 398        Ice.EncodingVersion encoding,
 399        TimeSpan invocationTimeout,
 400        Dictionary<string, string> context)
 401    {
 402        // Validate string arguments.
 403        Debug.Assert(facet != null);
 404
 405        _instance = instance;
 406        _communicator = communicator;
 407        _mode = mode;
 408        _identity = identity;
 409        _context = context != null ? new Dictionary<string, string>(context) : _emptyContext;
 410        _facet = facet;
 411        _protocol = protocol;
 412        _encoding = encoding;
 413        _invocationTimeout = invocationTimeout;
 414        _compress = compress;
 415    }
 416
 417    protected static Random rand_ = new Random(unchecked((int)DateTime.Now.Ticks));
 418}
 419
 420public class FixedReference : Reference
 421{
 422    internal override BatchRequestQueue batchRequestQueue => _fixedConnection.getBatchRequestQueue();
 423
 424    public FixedReference(
 425        Instance instance,
 426        Ice.Communicator communicator,
 427        Ice.Identity identity,
 428        string facet,
 429        Mode mode,
 430        bool? compress,
 431        Ice.ProtocolVersion protocol,
 432        Ice.EncodingVersion encoding,
 433        Ice.ConnectionI connection,
 434        TimeSpan invocationTimeout,
 435        Dictionary<string, string> context)
 436    : base(
 437        instance,
 438        communicator,
 439        identity,
 440        facet,
 441        mode,
 442        compress,
 443        protocol,
 444        encoding,
 445        invocationTimeout,
 446        context) =>
 447        _fixedConnection = connection;
 448
 449    public override EndpointI[] getEndpoints() => _emptyEndpoints;
 450
 451    public override string getAdapterId() => "";
 452
 453    public override LocatorInfo getLocatorInfo() => null;
 454
 455    public override RouterInfo getRouterInfo() => null;
 456
 457    public override bool getCollocationOptimized() => false;
 458
 459    public override bool getCacheConnection() => true;
 460
 461    public override Ice.EndpointSelectionType getEndpointSelection() => Ice.EndpointSelectionType.Random;
 462
 463    public override TimeSpan getLocatorCacheTimeout() => TimeSpan.Zero;
 464
 465    public override string getConnectionId() => "";
 466
 467    public override ThreadPool getThreadPool() => _fixedConnection.getThreadPool();
 468
 469    public override Connection getConnection() => _fixedConnection;
 470
 471    public override Reference changeEndpoints(EndpointI[] newEndpoints) => throw new Ice.FixedProxyException();
 472
 473    public override Reference changeAdapterId(string newAdapterId) => throw new Ice.FixedProxyException();
 474
 475    public override Reference changeLocator(Ice.LocatorPrx newLocator) => throw new Ice.FixedProxyException();
 476
 477    public override Reference changeRouter(Ice.RouterPrx newRouter) => throw new Ice.FixedProxyException();
 478
 479    public override Reference changeCollocationOptimized(bool newCollocationOptimized) =>
 480        throw new Ice.FixedProxyException();
 481
 482    public override Reference changeCacheConnection(bool newCache) => throw new Ice.FixedProxyException();
 483
 484    public override Reference changeEndpointSelection(Ice.EndpointSelectionType newType) =>
 485        throw new Ice.FixedProxyException();
 486
 487    public override Reference changeLocatorCacheTimeout(TimeSpan newTimeout) => throw new Ice.FixedProxyException();
 488
 489    public override Reference changeConnectionId(string connectionId) => throw new Ice.FixedProxyException();
 490
 491    public override Reference changeConnection(Ice.ConnectionI connection)
 492    {
 493        if (_fixedConnection == connection)
 494        {
 495            return this;
 496        }
 497        var r = (FixedReference)getInstance().referenceFactory().copy(this);
 498        r._fixedConnection = connection;
 499        return r;
 500    }
 501
 502    public override bool isIndirect() => false;
 503
 504    public override bool isWellKnown() => false;
 505
 506    public override void streamWrite(Ice.OutputStream s) => throw new Ice.FixedProxyException();
 507
 508    public override Dictionary<string, string> toProperty(string prefix) => throw new Ice.FixedProxyException();
 509
 510    internal override RequestHandler getRequestHandler()
 511    {
 512        // We need to perform all these checks here and not in the constructor because changeConnection() clones then
 513        // sets the connection.
 514
 515        switch (getMode())
 516        {
 517            case Mode.ModeTwoway:
 518            case Mode.ModeOneway:
 519            case Mode.ModeBatchOneway:
 520            {
 521                if (_fixedConnection.endpoint().datagram())
 522                {
 523                    throw new NoEndpointException(new ObjectPrxHelper(this));
 524                }
 525                break;
 526            }
 527
 528            case Mode.ModeDatagram:
 529            case Mode.ModeBatchDatagram:
 530            {
 531                if (!_fixedConnection.endpoint().datagram())
 532                {
 533                    throw new NoEndpointException(new ObjectPrxHelper(this));
 534                }
 535                break;
 536            }
 537        }
 538
 539        _fixedConnection.throwException(); // Throw in case our connection is already destroyed.
 540
 541        DefaultsAndOverrides defaultsAndOverrides = getInstance().defaultsAndOverrides();
 542        bool compress = defaultsAndOverrides.overrideCompress ?? getCompress() ?? false;
 543        return new FixedRequestHandler(this, _fixedConnection, compress);
 544    }
 545
 546    public override bool Equals(Reference other)
 547    {
 548        if (ReferenceEquals(this, other))
 549        {
 550            return true;
 551        }
 552        var rhs = other as FixedReference;
 553        return rhs is not null && base.Equals(rhs) && _fixedConnection.Equals(rhs._fixedConnection);
 554    }
 555
 556    private static readonly EndpointI[] _emptyEndpoints = [];
 557    private Ice.ConnectionI _fixedConnection;
 558}
 559
 560public class RoutableReference : Reference
 561{
 562    internal override BatchRequestQueue batchRequestQueue => _batchRequestQueue;
 563
 564    public override EndpointI[] getEndpoints() => _endpoints;
 565
 566    public override string getAdapterId() => _adapterId;
 567
 568    public override LocatorInfo getLocatorInfo() => _locatorInfo;
 569
 570    public override RouterInfo getRouterInfo() => _routerInfo;
 571
 572    public override bool getCollocationOptimized() => _collocationOptimized;
 573
 574    public override bool getCacheConnection() => _cacheConnection;
 575
 576    public override Ice.EndpointSelectionType getEndpointSelection() => _endpointSelection;
 577
 578    public override TimeSpan getLocatorCacheTimeout() => _locatorCacheTimeout;
 579
 580    public override string getConnectionId() => _connectionId;
 581
 582    public override Reference changeMode(Mode newMode)
 583    {
 584        Reference r = base.changeMode(newMode);
 585        ((RoutableReference)r).setBatchRequestQueue();
 586        return r;
 587    }
 588
 589    public override ThreadPool getThreadPool() => getInstance().clientThreadPool();
 590
 591    public override Connection getConnection() => null;
 592
 593    public override Reference changeEncoding(Ice.EncodingVersion newEncoding)
 594    {
 595        var r = (RoutableReference)base.changeEncoding(newEncoding);
 596        if (r != this)
 597        {
 598            LocatorInfo locInfo = r._locatorInfo;
 599            if (locInfo != null && !locInfo.getLocator().ice_getEncodingVersion().Equals(newEncoding))
 600            {
 601                r._locatorInfo = getInstance().locatorManager().get(
 602                    (Ice.LocatorPrx)locInfo.getLocator().ice_encodingVersion(newEncoding));
 603            }
 604        }
 605        return r;
 606    }
 607
 608    public override Reference changeCompress(bool newCompress)
 609    {
 610        var r = (RoutableReference)base.changeCompress(newCompress);
 611        if (r != this && _endpoints.Length > 0) // Also override the compress flag on the endpoints if it was updated
 612        {
 613            var newEndpoints = new EndpointI[_endpoints.Length];
 614            for (int i = 0; i < _endpoints.Length; i++)
 615            {
 616                newEndpoints[i] = _endpoints[i].compress(newCompress);
 617            }
 618            r._endpoints = newEndpoints;
 619        }
 620        return r;
 621    }
 622
 623    public override Reference changeEndpoints(EndpointI[] newEndpoints)
 624    {
 625        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 626        r._endpoints = newEndpoints;
 627        r._adapterId = "";
 628        r.applyOverrides(ref r._endpoints);
 629        return r;
 630    }
 631
 632    public override Reference changeAdapterId(string newAdapterId)
 633    {
 634        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 635        r._adapterId = newAdapterId;
 636        r._endpoints = _emptyEndpoints;
 637        return r;
 638    }
 639
 640    public override Reference changeLocator(Ice.LocatorPrx newLocator)
 641    {
 642        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 643        r._locatorInfo = getInstance().locatorManager().get(newLocator);
 644        return r;
 645    }
 646
 647    public override Reference changeRouter(Ice.RouterPrx newRouter)
 648    {
 649        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 650        r._routerInfo = getInstance().routerManager().get(newRouter);
 651        return r;
 652    }
 653
 654    public override Reference changeCollocationOptimized(bool newCollocationOptimized)
 655    {
 656        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 657        r._collocationOptimized = newCollocationOptimized;
 658        return r;
 659    }
 660
 661    public override Reference changeCacheConnection(bool newCache)
 662    {
 663        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 664        r._cacheConnection = newCache;
 665        return r;
 666    }
 667
 668    public override Reference changeEndpointSelection(Ice.EndpointSelectionType newType)
 669    {
 670        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 671        r._endpointSelection = newType;
 672        return r;
 673    }
 674
 675    public override Reference changeLocatorCacheTimeout(TimeSpan newTimeout)
 676    {
 677        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 678        r._locatorCacheTimeout = newTimeout;
 679        return r;
 680    }
 681
 682    public override Reference changeConnectionId(string connectionId)
 683    {
 684        var r = (RoutableReference)getInstance().referenceFactory().copy(this);
 685        r._connectionId = connectionId;
 686        if (_endpoints.Length > 0)
 687        {
 688            var newEndpoints = new EndpointI[_endpoints.Length];
 689            for (int i = 0; i < _endpoints.Length; i++)
 690            {
 691                newEndpoints[i] = _endpoints[i].connectionId(connectionId);
 692            }
 693            r._endpoints = newEndpoints;
 694        }
 695        return r;
 696    }
 697
 698    public override Reference changeConnection(Ice.ConnectionI connection)
 699    {
 700        return new FixedReference(
 701            getInstance(),
 702            getCommunicator(),
 703            getIdentity(),
 704            getFacet(),
 705            getMode(),
 706            getCompress(),
 707            getProtocol(),
 708            getEncoding(),
 709            connection,
 710            getInvocationTimeout(),
 711            getContext());
 712    }
 713
 714    public override bool isIndirect() => _endpoints.Length == 0;
 715
 716    public override bool isWellKnown() => _endpoints.Length == 0 && _adapterId.Length == 0;
 717
 718    public override void streamWrite(Ice.OutputStream s)
 719    {
 720        base.streamWrite(s);
 721
 722        s.writeSize(_endpoints.Length);
 723        if (_endpoints.Length > 0)
 724        {
 725            Debug.Assert(_adapterId.Length == 0);
 726            foreach (EndpointI endpoint in _endpoints)
 727            {
 728                s.writeShort(endpoint.type());
 729                endpoint.streamWrite(s);
 730            }
 731        }
 732        else
 733        {
 734            s.writeString(_adapterId); // Adapter id.
 735        }
 736    }
 737
 738    public override string ToString()
 739    {
 740        //
 741        // WARNING: Certain features, such as proxy validation in Glacier2,
 742        // depend on the format of proxy strings. Changes to toString() and
 743        // methods called to generate parts of the reference string could break
 744        // these features. Please review for all features that depend on the
 745        // format of proxyToString() before changing this and related code.
 746        //
 747        var s = new StringBuilder();
 748        s.Append(base.ToString());
 749
 750        if (_endpoints.Length > 0)
 751        {
 752            for (int i = 0; i < _endpoints.Length; i++)
 753            {
 754                string endp = _endpoints[i].ToString();
 755                if (endp != null && endp.Length > 0)
 756                {
 757                    s.Append(':');
 758                    s.Append(endp);
 759                }
 760            }
 761        }
 762        else if (_adapterId.Length > 0)
 763        {
 764            s.Append(" @ ");
 765
 766            //
 767            // If the encoded adapter id string contains characters which
 768            // the reference parser uses as separators, then we enclose
 769            // the adapter id string in quotes.
 770            //
 771            string a = Ice.UtilInternal.StringUtil.escapeString(_adapterId, null, getInstance().toStringMode());
 772            if (Ice.UtilInternal.StringUtil.findFirstOf(a, " :@") != -1)
 773            {
 774                s.Append('"');
 775                s.Append(a);
 776                s.Append('"');
 777            }
 778            else
 779            {
 780                s.Append(a);
 781            }
 782        }
 783        return s.ToString();
 784    }
 785
 786    public override Dictionary<string, string> toProperty(string prefix)
 787    {
 788        var properties = new Dictionary<string, string>
 789        {
 790            [prefix] = ToString(),
 791            [prefix + ".CollocationOptimized"] = _collocationOptimized ? "1" : "0",
 792            [prefix + ".ConnectionCached"] = _cacheConnection ? "1" : "0",
 793            [prefix + ".EndpointSelection"] =
 794                   _endpointSelection == Ice.EndpointSelectionType.Random ? "Random" : "Ordered",
 795            [prefix + ".LocatorCacheTimeout"] =
 796                _locatorCacheTimeout.TotalSeconds.ToString(CultureInfo.InvariantCulture),
 797            [prefix + ".InvocationTimeout"] =
 798                getInvocationTimeout().TotalMilliseconds.ToString(CultureInfo.InvariantCulture)
 799        };
 800
 801        if (_routerInfo != null)
 802        {
 803            var h = (Ice.ObjectPrxHelperBase)_routerInfo.getRouter();
 804            Dictionary<string, string> routerProperties = h.iceReference().toProperty(prefix + ".Router");
 805            foreach (KeyValuePair<string, string> entry in routerProperties)
 806            {
 807                properties[entry.Key] = entry.Value;
 808            }
 809        }
 810
 811        if (_locatorInfo != null)
 812        {
 813            var h = (Ice.ObjectPrxHelperBase)_locatorInfo.getLocator();
 814            Dictionary<string, string> locatorProperties = h.iceReference().toProperty(prefix + ".Locator");
 815            foreach (KeyValuePair<string, string> entry in locatorProperties)
 816            {
 817                properties[entry.Key] = entry.Value;
 818            }
 819        }
 820
 821        return properties;
 822    }
 823
 824    public override int GetHashCode()
 825    {
 826        var hash = new HashCode();
 827        hash.Add(base.GetHashCode());
 828        hash.Add(_adapterId);
 829        foreach (EndpointI endpoint in _endpoints)
 830        {
 831            hash.Add(endpoint);
 832        }
 833        return hash.ToHashCode();
 834    }
 835
 836    public override bool Equals(Reference other)
 837    {
 838        if (ReferenceEquals(this, other))
 839        {
 840            return true;
 841        }
 842        var rhs = other as RoutableReference;
 843
 844        return rhs is not null &&
 845            base.Equals(rhs) &&
 846            _locatorInfo == rhs._locatorInfo &&
 847            _routerInfo == rhs._routerInfo &&
 848            _collocationOptimized == rhs._collocationOptimized &&
 849            _cacheConnection == rhs._cacheConnection &&
 850            _endpointSelection == rhs._endpointSelection &&
 851            _locatorCacheTimeout == rhs._locatorCacheTimeout &&
 852            _connectionId == rhs._connectionId &&
 853            _adapterId == rhs._adapterId &&
 854            _endpoints.SequenceEqual(rhs._endpoints);
 855    }
 856
 857    public override Reference Clone()
 858    {
 859        var clone = (RoutableReference)MemberwiseClone();
 860        // Each reference gets its own batch request queue.
 861        clone.setBatchRequestQueue();
 862        return clone;
 863    }
 864
 865    private sealed class RouterEndpointsCallback : RouterInfo.GetClientEndpointsCallback
 866    {
 867        internal RouterEndpointsCallback(RoutableReference ir, GetConnectionCallback cb)
 868        {
 869            _ir = ir;
 870            _cb = cb;
 871        }
 872
 873        public void setEndpoints(EndpointI[] endpoints)
 874        {
 875            if (endpoints.Length > 0)
 876            {
 877                _ir.applyOverrides(ref endpoints);
 878                _ir.createConnection(endpoints, _cb);
 879            }
 880            else
 881            {
 882                _ir.getConnectionNoRouterInfo(_cb);
 883            }
 884        }
 885
 886        public void setException(Ice.LocalException ex) => _cb.setException(ex);
 887
 888        private readonly RoutableReference _ir;
 889        private readonly GetConnectionCallback _cb;
 890    }
 891
 892    internal override RequestHandler getRequestHandler()
 893    {
 894        if (_collocationOptimized)
 895        {
 896            if (_instance.objectAdapterFactory().findObjectAdapter(this) is ObjectAdapter adapter)
 897            {
 898                return new CollocatedRequestHandler(this, adapter);
 899            }
 900        }
 901
 902        var handler = new ConnectRequestHandler(this);
 903        getConnection(handler);
 904        return handler;
 905    }
 906
 907    public void getConnection(GetConnectionCallback callback)
 908    {
 909        if (_routerInfo != null)
 910        {
 911            //
 912            // If we route, we send everything to the router's client
 913            // proxy endpoints.
 914            //
 915            _routerInfo.getClientEndpoints(new RouterEndpointsCallback(this, callback));
 916        }
 917        else
 918        {
 919            getConnectionNoRouterInfo(callback);
 920        }
 921    }
 922
 923    private sealed class LocatorEndpointsCallback : LocatorInfo.GetEndpointsCallback
 924    {
 1925        internal LocatorEndpointsCallback(RoutableReference ir, GetConnectionCallback cb)
 926        {
 1927            _ir = ir;
 1928            _cb = cb;
 1929        }
 930
 931        public void setEndpoints(EndpointI[] endpoints, bool cached)
 932        {
 1933            if (endpoints.Length == 0)
 934            {
 1935                _cb.setException(new NoEndpointException(new ObjectPrxHelper(_ir)));
 1936                return;
 937            }
 938
 1939            _ir.applyOverrides(ref endpoints);
 1940            _ir.createConnection(endpoints, new ConnectionCallback(_ir, _cb, cached));
 1941        }
 942
 1943        public void setException(Ice.LocalException ex) => _cb.setException(ex);
 944
 945        private readonly RoutableReference _ir;
 946        private readonly GetConnectionCallback _cb;
 947    }
 948
 949    private sealed class ConnectionCallback : GetConnectionCallback
 950    {
 951        internal ConnectionCallback(RoutableReference ir, GetConnectionCallback cb, bool cached)
 952        {
 953            _ir = ir;
 954            _cb = cb;
 955            _cached = cached;
 956        }
 957
 958        public void setConnection(Ice.ConnectionI connection, bool compress) =>
 959            _cb.setConnection(connection, compress);
 960
 961        public void setException(Ice.LocalException exc)
 962        {
 963            try
 964            {
 965                throw exc;
 966            }
 967            catch (NoEndpointException ex)
 968            {
 969                _cb.setException(ex); // No need to retry if there's no endpoints.
 970            }
 971            catch (Ice.LocalException ex)
 972            {
 973                Debug.Assert(_ir._locatorInfo != null);
 974                _ir._locatorInfo.clearCache(_ir);
 975                if (_cached)
 976                {
 977                    TraceLevels traceLevels = _ir.getInstance().traceLevels();
 978                    if (traceLevels.retry >= 2)
 979                    {
 980                        string s = "connection to cached endpoints failed\n" +
 981                                   "removing endpoints from cache and trying again\n" + ex;
 982                        _ir.getInstance().initializationData().logger.trace(traceLevels.retryCat, s);
 983                    }
 984                    _ir.getConnectionNoRouterInfo(_cb); // Retry.
 985                    return;
 986                }
 987                _cb.setException(ex);
 988            }
 989        }
 990
 991        private readonly RoutableReference _ir;
 992        private readonly GetConnectionCallback _cb;
 993        private readonly bool _cached;
 994    }
 995
 996    private void getConnectionNoRouterInfo(GetConnectionCallback callback)
 997    {
 998        if (_endpoints.Length > 0)
 999        {
 1000            createConnection(_endpoints, callback);
 1001            return;
 1002        }
 1003
 1004        if (_locatorInfo != null)
 1005        {
 1006            _locatorInfo.getEndpoints(this, _locatorCacheTimeout, new LocatorEndpointsCallback(this, callback));
 1007        }
 1008        else
 1009        {
 1010            callback.setException(new NoEndpointException(new ObjectPrxHelper(this)));
 1011        }
 1012    }
 1013
 1014    public RoutableReference(
 1015        Instance instance,
 1016        Ice.Communicator communicator,
 1017        Ice.Identity identity,
 1018        string facet,
 1019        Mode mode,
 1020        bool? compress,
 1021        Ice.ProtocolVersion protocol,
 1022        Ice.EncodingVersion encoding,
 1023        EndpointI[] endpoints,
 1024        string adapterId,
 1025        LocatorInfo locatorInfo,
 1026        RouterInfo routerInfo,
 1027        bool collocationOptimized,
 1028        bool cacheConnection,
 1029        Ice.EndpointSelectionType endpointSelection,
 1030        TimeSpan locatorCacheTimeout,
 1031        TimeSpan invocationTimeout,
 1032        Dictionary<string, string> context)
 1033    : base(
 1034        instance,
 1035        communicator,
 1036        identity,
 1037        facet,
 1038        mode,
 1039        compress,
 1040        protocol,
 1041        encoding,
 1042        invocationTimeout,
 1043        context)
 1044    {
 1045        _endpoints = endpoints;
 1046        _adapterId = adapterId;
 1047        _locatorInfo = locatorInfo;
 1048        _routerInfo = routerInfo;
 1049        _collocationOptimized = collocationOptimized;
 1050        _cacheConnection = cacheConnection;
 1051        _endpointSelection = endpointSelection;
 1052        _locatorCacheTimeout = locatorCacheTimeout;
 1053
 1054        _endpoints ??= _emptyEndpoints;
 1055
 1056        _adapterId ??= "";
 1057        setBatchRequestQueue();
 1058
 1059        Debug.Assert(_adapterId.Length == 0 || _endpoints.Length == 0);
 1060    }
 1061
 1062    protected void applyOverrides(ref EndpointI[] endpoints)
 1063    {
 1064        for (int i = 0; i < endpoints.Length; ++i)
 1065        {
 1066            endpoints[i] = endpoints[i].connectionId(_connectionId);
 1067            bool? compress = getCompress();
 1068            if (compress is not null)
 1069            {
 1070                endpoints[i] = endpoints[i].compress(compress.Value);
 1071            }
 1072        }
 1073    }
 1074
 1075    private EndpointI[] filterEndpoints(EndpointI[] allEndpoints)
 1076    {
 1077        var endpoints = new List<EndpointI>();
 1078
 1079        //
 1080        // Filter out unknown endpoints.
 1081        //
 1082        for (int i = 0; i < allEndpoints.Length; i++)
 1083        {
 1084            if (!(allEndpoints[i] is OpaqueEndpointI))
 1085            {
 1086                endpoints.Add(allEndpoints[i]);
 1087            }
 1088        }
 1089
 1090        //
 1091        // Filter out endpoints according to the mode of the reference.
 1092        //
 1093        switch (getMode())
 1094        {
 1095            case Mode.ModeTwoway:
 1096            case Mode.ModeOneway:
 1097            case Mode.ModeBatchOneway:
 1098            {
 1099                //
 1100                // Filter out datagram endpoints.
 1101                //
 1102                var tmp = new List<EndpointI>();
 1103                foreach (EndpointI endpoint in endpoints)
 1104                {
 1105                    if (!endpoint.datagram())
 1106                    {
 1107                        tmp.Add(endpoint);
 1108                    }
 1109                }
 1110                endpoints = tmp;
 1111                break;
 1112            }
 1113
 1114            case Mode.ModeDatagram:
 1115            case Mode.ModeBatchDatagram:
 1116            {
 1117                //
 1118                // Filter out non-datagram endpoints.
 1119                //
 1120                var tmp = new List<EndpointI>();
 1121                foreach (EndpointI endpoint in endpoints)
 1122                {
 1123                    if (endpoint.datagram())
 1124                    {
 1125                        tmp.Add(endpoint);
 1126                    }
 1127                }
 1128                endpoints = tmp;
 1129                break;
 1130            }
 1131        }
 1132
 1133        //
 1134        // Sort the endpoints according to the endpoint selection type.
 1135        //
 1136        switch (getEndpointSelection())
 1137        {
 1138            case Ice.EndpointSelectionType.Random:
 1139            {
 1140                lock (rand_)
 1141                {
 1142                    for (int i = 0; i < endpoints.Count - 1; ++i)
 1143                    {
 1144                        int r = rand_.Next(endpoints.Count - i) + i;
 1145                        Debug.Assert(r >= i && r < endpoints.Count);
 1146                        if (r != i)
 1147                        {
 1148                            EndpointI tmp = endpoints[i];
 1149                            endpoints[i] = endpoints[r];
 1150                            endpoints[r] = tmp;
 1151                        }
 1152                    }
 1153                }
 1154                break;
 1155            }
 1156            case Ice.EndpointSelectionType.Ordered:
 1157            {
 1158                // Nothing to do.
 1159                break;
 1160            }
 1161            default:
 1162            {
 1163                Debug.Assert(false);
 1164                break;
 1165            }
 1166        }
 1167
 1168        var arr = new EndpointI[endpoints.Count];
 1169        endpoints.CopyTo(arr);
 1170        return arr;
 1171    }
 1172
 1173    private sealed class CreateConnectionCallback : OutgoingConnectionFactory.CreateConnectionCallback
 1174    {
 1175        internal CreateConnectionCallback(RoutableReference rr, EndpointI[] endpoints, GetConnectionCallback cb)
 1176        {
 1177            _rr = rr;
 1178            _endpoints = endpoints;
 1179            _callback = cb;
 1180        }
 1181
 1182        public void setConnection(Ice.ConnectionI connection, bool compress)
 1183        {
 1184            //
 1185            // If we have a router, set the object adapter for this router
 1186            // (if any) to the new connection, so that callbacks from the
 1187            // router can be received over this new connection.
 1188            //
 1189            if (_rr._routerInfo != null && _rr._routerInfo.getAdapter() != null)
 1190            {
 1191                connection.setAdapter(_rr._routerInfo.getAdapter());
 1192            }
 1193            _callback.setConnection(connection, compress);
 1194        }
 1195
 1196        public void setException(Ice.LocalException ex)
 1197        {
 1198            _exception ??= ex;
 1199
 1200            if (_endpoints == null || ++_i == _endpoints.Length)
 1201            {
 1202                _callback.setException(_exception);
 1203                return;
 1204            }
 1205
 1206            bool more = _i != _endpoints.Length - 1;
 1207            var endpointList = new List<EndpointI> { _endpoints[_i] };
 1208            _rr.getInstance().outgoingConnectionFactory().create(endpointList, more, this);
 1209        }
 1210
 1211        private readonly RoutableReference _rr;
 1212        private readonly EndpointI[] _endpoints;
 1213        private readonly GetConnectionCallback _callback;
 1214        private int _i;
 1215        private Ice.LocalException _exception;
 1216    }
 1217
 1218    protected void createConnection(EndpointI[] allEndpoints, GetConnectionCallback callback)
 1219    {
 1220        EndpointI[] endpoints = filterEndpoints(allEndpoints);
 1221        if (endpoints.Length == 0)
 1222        {
 1223            callback.setException(new NoEndpointException(new ObjectPrxHelper(this)));
 1224            return;
 1225        }
 1226
 1227        //
 1228        // Finally, create the connection.
 1229        //
 1230        OutgoingConnectionFactory factory = getInstance().outgoingConnectionFactory();
 1231        if (getCacheConnection() || endpoints.Length == 1)
 1232        {
 1233            //
 1234            // Get an existing connection or create one if there's no
 1235            // existing connection to one of the given endpoints.
 1236            //
 1237            factory.create(endpoints.ToList(), false, new CreateConnectionCallback(this, null, callback));
 1238        }
 1239        else
 1240        {
 1241            //
 1242            // Go through the list of endpoints and try to create the
 1243            // connection until it succeeds. This is different from just
 1244            // calling create() with the given endpoints since this might
 1245            // create a new connection even if there's an existing
 1246            // connection for one of the endpoints.
 1247            //
 1248
 1249            factory.create(
 1250                new List<EndpointI> { endpoints[0] },
 1251                true,
 1252                new CreateConnectionCallback(this, endpoints, callback));
 1253        }
 1254    }
 1255
 1256    // Sets or resets _batchRequestQueue based on _mode.
 1257    private void setBatchRequestQueue()
 1258    {
 1259        _batchRequestQueue =
 1260            isBatch ? new BatchRequestQueue(getInstance(), getMode() == Mode.ModeBatchDatagram) : null;
 1261    }
 1262
 1263    private static readonly EndpointI[] _emptyEndpoints = [];
 1264
 1265    private BatchRequestQueue _batchRequestQueue;
 1266
 1267    private EndpointI[] _endpoints;
 1268    private string _adapterId;
 1269    private LocatorInfo _locatorInfo; // Null if no locator is used.
 1270    private RouterInfo _routerInfo; // Null if no router is used.
 1271    private bool _collocationOptimized;
 1272    private bool _cacheConnection;
 1273    private Ice.EndpointSelectionType _endpointSelection;
 1274    private TimeSpan _locatorCacheTimeout;
 1275
 1276    private string _connectionId = "";
 1277}