< Summary

Information
Class: Ice.Internal.RoutableReference.ConnectionCallback
Assembly: Ice
File(s): /_/csharp/src/Ice/Internal/Reference.cs
Tag: 125_37167941578
Line coverage
73%
Covered lines: 17
Uncovered lines: 6
Coverable lines: 23
Total lines: 1279
Line coverage: 73.9%
Branch coverage
50%
Covered branches: 3
Total branches: 6
Branch coverage: 50%
Method coverage
100%
Covered methods: 3
Fully covered methods: 2
Total methods: 3
Method coverage: 100%
Full method coverage: 66.6%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
setConnection(...)100%11100%
setException(...)50%8664.71%

File(s)

/_/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 != Protocol.Protocol_1_0)
 317        {
 318            // We print the protocol unless it's 1.0.
 319            s.Append(" -p ");
 320            s.Append(Protocol.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(Protocol.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            // The timeouts are always normalized: infinite is -1 and a positive timeout is a whole number of the
 796            // property's unit.
 797            [prefix + ".LocatorCacheTimeout"] =
 798                ((long)_locatorCacheTimeout.TotalSeconds).ToString(CultureInfo.InvariantCulture),
 799            [prefix + ".InvocationTimeout"] =
 800                ((long)getInvocationTimeout().TotalMilliseconds).ToString(CultureInfo.InvariantCulture)
 801        };
 802
 803        if (_routerInfo != null)
 804        {
 805            var h = (Ice.ObjectPrxHelperBase)_routerInfo.getRouter();
 806            Dictionary<string, string> routerProperties = h.iceReference().toProperty(prefix + ".Router");
 807            foreach (KeyValuePair<string, string> entry in routerProperties)
 808            {
 809                properties[entry.Key] = entry.Value;
 810            }
 811        }
 812
 813        if (_locatorInfo != null)
 814        {
 815            var h = (Ice.ObjectPrxHelperBase)_locatorInfo.getLocator();
 816            Dictionary<string, string> locatorProperties = h.iceReference().toProperty(prefix + ".Locator");
 817            foreach (KeyValuePair<string, string> entry in locatorProperties)
 818            {
 819                properties[entry.Key] = entry.Value;
 820            }
 821        }
 822
 823        return properties;
 824    }
 825
 826    public override int GetHashCode()
 827    {
 828        var hash = new HashCode();
 829        hash.Add(base.GetHashCode());
 830        hash.Add(_adapterId);
 831        foreach (EndpointI endpoint in _endpoints)
 832        {
 833            hash.Add(endpoint);
 834        }
 835        return hash.ToHashCode();
 836    }
 837
 838    public override bool Equals(Reference other)
 839    {
 840        if (ReferenceEquals(this, other))
 841        {
 842            return true;
 843        }
 844        var rhs = other as RoutableReference;
 845
 846        return rhs is not null &&
 847            base.Equals(rhs) &&
 848            _locatorInfo == rhs._locatorInfo &&
 849            _routerInfo == rhs._routerInfo &&
 850            _collocationOptimized == rhs._collocationOptimized &&
 851            _cacheConnection == rhs._cacheConnection &&
 852            _endpointSelection == rhs._endpointSelection &&
 853            _locatorCacheTimeout == rhs._locatorCacheTimeout &&
 854            _connectionId == rhs._connectionId &&
 855            _adapterId == rhs._adapterId &&
 856            _endpoints.SequenceEqual(rhs._endpoints);
 857    }
 858
 859    public override Reference Clone()
 860    {
 861        var clone = (RoutableReference)MemberwiseClone();
 862        // Each reference gets its own batch request queue.
 863        clone.setBatchRequestQueue();
 864        return clone;
 865    }
 866
 867    private sealed class RouterEndpointsCallback : RouterInfo.GetClientEndpointsCallback
 868    {
 869        internal RouterEndpointsCallback(RoutableReference ir, GetConnectionCallback cb)
 870        {
 871            _ir = ir;
 872            _cb = cb;
 873        }
 874
 875        public void setEndpoints(EndpointI[] endpoints)
 876        {
 877            if (endpoints.Length > 0)
 878            {
 879                _ir.applyOverrides(ref endpoints);
 880                _ir.createConnection(endpoints, _cb);
 881            }
 882            else
 883            {
 884                _ir.getConnectionNoRouterInfo(_cb);
 885            }
 886        }
 887
 888        public void setException(Ice.LocalException ex) => _cb.setException(ex);
 889
 890        private readonly RoutableReference _ir;
 891        private readonly GetConnectionCallback _cb;
 892    }
 893
 894    internal override RequestHandler getRequestHandler()
 895    {
 896        if (_collocationOptimized)
 897        {
 898            if (_instance.objectAdapterFactory().findObjectAdapter(this) is ObjectAdapter adapter)
 899            {
 900                return new CollocatedRequestHandler(this, adapter);
 901            }
 902        }
 903
 904        var handler = new ConnectRequestHandler(this);
 905        getConnection(handler);
 906        return handler;
 907    }
 908
 909    public void getConnection(GetConnectionCallback callback)
 910    {
 911        if (_routerInfo != null)
 912        {
 913            //
 914            // If we route, we send everything to the router's client
 915            // proxy endpoints.
 916            //
 917            _routerInfo.getClientEndpoints(new RouterEndpointsCallback(this, callback));
 918        }
 919        else
 920        {
 921            getConnectionNoRouterInfo(callback);
 922        }
 923    }
 924
 925    private sealed class LocatorEndpointsCallback : LocatorInfo.GetEndpointsCallback
 926    {
 927        internal LocatorEndpointsCallback(RoutableReference ir, GetConnectionCallback cb)
 928        {
 929            _ir = ir;
 930            _cb = cb;
 931        }
 932
 933        public void setEndpoints(EndpointI[] endpoints, bool cached)
 934        {
 935            if (endpoints.Length == 0)
 936            {
 937                _cb.setException(new NoEndpointException(new ObjectPrxHelper(_ir)));
 938                return;
 939            }
 940
 941            _ir.applyOverrides(ref endpoints);
 942            _ir.createConnection(endpoints, new ConnectionCallback(_ir, _cb, cached));
 943        }
 944
 945        public void setException(Ice.LocalException ex) => _cb.setException(ex);
 946
 947        private readonly RoutableReference _ir;
 948        private readonly GetConnectionCallback _cb;
 949    }
 950
 951    private sealed class ConnectionCallback : GetConnectionCallback
 952    {
 1953        internal ConnectionCallback(RoutableReference ir, GetConnectionCallback cb, bool cached)
 954        {
 1955            _ir = ir;
 1956            _cb = cb;
 1957            _cached = cached;
 1958        }
 959
 960        public void setConnection(Ice.ConnectionI connection, bool compress) =>
 1961            _cb.setConnection(connection, compress);
 962
 963        public void setException(Ice.LocalException exc)
 964        {
 965            try
 966            {
 1967                throw exc;
 968            }
 0969            catch (NoEndpointException ex)
 970            {
 0971                _cb.setException(ex); // No need to retry if there's no endpoints.
 0972            }
 1973            catch (Ice.LocalException ex)
 974            {
 975                Debug.Assert(_ir._locatorInfo != null);
 1976                _ir._locatorInfo.clearCache(_ir);
 1977                if (_cached)
 978                {
 1979                    TraceLevels traceLevels = _ir.getInstance().traceLevels();
 1980                    if (traceLevels.retry >= 2)
 981                    {
 0982                        string s = "connection to cached endpoints failed\n" +
 0983                                   "removing endpoints from cache and trying again\n" + ex;
 0984                        _ir.getInstance().initializationData().logger.trace(traceLevels.retryCat, s);
 985                    }
 1986                    _ir.getConnectionNoRouterInfo(_cb); // Retry.
 1987                    return;
 988                }
 1989                _cb.setException(ex);
 1990            }
 1991        }
 992
 993        private readonly RoutableReference _ir;
 994        private readonly GetConnectionCallback _cb;
 995        private readonly bool _cached;
 996    }
 997
 998    private void getConnectionNoRouterInfo(GetConnectionCallback callback)
 999    {
 1000        if (_endpoints.Length > 0)
 1001        {
 1002            createConnection(_endpoints, callback);
 1003            return;
 1004        }
 1005
 1006        if (_locatorInfo != null)
 1007        {
 1008            _locatorInfo.getEndpoints(this, _locatorCacheTimeout, new LocatorEndpointsCallback(this, callback));
 1009        }
 1010        else
 1011        {
 1012            callback.setException(new NoEndpointException(new ObjectPrxHelper(this)));
 1013        }
 1014    }
 1015
 1016    public RoutableReference(
 1017        Instance instance,
 1018        Ice.Communicator communicator,
 1019        Ice.Identity identity,
 1020        string facet,
 1021        Mode mode,
 1022        bool? compress,
 1023        Ice.ProtocolVersion protocol,
 1024        Ice.EncodingVersion encoding,
 1025        EndpointI[] endpoints,
 1026        string adapterId,
 1027        LocatorInfo locatorInfo,
 1028        RouterInfo routerInfo,
 1029        bool collocationOptimized,
 1030        bool cacheConnection,
 1031        Ice.EndpointSelectionType endpointSelection,
 1032        TimeSpan locatorCacheTimeout,
 1033        TimeSpan invocationTimeout,
 1034        Dictionary<string, string> context)
 1035    : base(
 1036        instance,
 1037        communicator,
 1038        identity,
 1039        facet,
 1040        mode,
 1041        compress,
 1042        protocol,
 1043        encoding,
 1044        invocationTimeout,
 1045        context)
 1046    {
 1047        _endpoints = endpoints;
 1048        _adapterId = adapterId;
 1049        _locatorInfo = locatorInfo;
 1050        _routerInfo = routerInfo;
 1051        _collocationOptimized = collocationOptimized;
 1052        _cacheConnection = cacheConnection;
 1053        _endpointSelection = endpointSelection;
 1054        _locatorCacheTimeout = locatorCacheTimeout;
 1055
 1056        _endpoints ??= _emptyEndpoints;
 1057
 1058        _adapterId ??= "";
 1059        setBatchRequestQueue();
 1060
 1061        Debug.Assert(_adapterId.Length == 0 || _endpoints.Length == 0);
 1062    }
 1063
 1064    protected void applyOverrides(ref EndpointI[] endpoints)
 1065    {
 1066        for (int i = 0; i < endpoints.Length; ++i)
 1067        {
 1068            endpoints[i] = endpoints[i].connectionId(_connectionId);
 1069            bool? compress = getCompress();
 1070            if (compress is not null)
 1071            {
 1072                endpoints[i] = endpoints[i].compress(compress.Value);
 1073            }
 1074        }
 1075    }
 1076
 1077    private EndpointI[] filterEndpoints(EndpointI[] allEndpoints)
 1078    {
 1079        var endpoints = new List<EndpointI>();
 1080
 1081        //
 1082        // Filter out unknown endpoints.
 1083        //
 1084        for (int i = 0; i < allEndpoints.Length; i++)
 1085        {
 1086            if (!(allEndpoints[i] is OpaqueEndpointI))
 1087            {
 1088                endpoints.Add(allEndpoints[i]);
 1089            }
 1090        }
 1091
 1092        //
 1093        // Filter out endpoints according to the mode of the reference.
 1094        //
 1095        switch (getMode())
 1096        {
 1097            case Mode.ModeTwoway:
 1098            case Mode.ModeOneway:
 1099            case Mode.ModeBatchOneway:
 1100            {
 1101                //
 1102                // Filter out datagram endpoints.
 1103                //
 1104                var tmp = new List<EndpointI>();
 1105                foreach (EndpointI endpoint in endpoints)
 1106                {
 1107                    if (!endpoint.datagram())
 1108                    {
 1109                        tmp.Add(endpoint);
 1110                    }
 1111                }
 1112                endpoints = tmp;
 1113                break;
 1114            }
 1115
 1116            case Mode.ModeDatagram:
 1117            case Mode.ModeBatchDatagram:
 1118            {
 1119                //
 1120                // Filter out non-datagram endpoints.
 1121                //
 1122                var tmp = new List<EndpointI>();
 1123                foreach (EndpointI endpoint in endpoints)
 1124                {
 1125                    if (endpoint.datagram())
 1126                    {
 1127                        tmp.Add(endpoint);
 1128                    }
 1129                }
 1130                endpoints = tmp;
 1131                break;
 1132            }
 1133        }
 1134
 1135        //
 1136        // Sort the endpoints according to the endpoint selection type.
 1137        //
 1138        switch (getEndpointSelection())
 1139        {
 1140            case Ice.EndpointSelectionType.Random:
 1141            {
 1142                lock (rand_)
 1143                {
 1144                    for (int i = 0; i < endpoints.Count - 1; ++i)
 1145                    {
 1146                        int r = rand_.Next(endpoints.Count - i) + i;
 1147                        Debug.Assert(r >= i && r < endpoints.Count);
 1148                        if (r != i)
 1149                        {
 1150                            EndpointI tmp = endpoints[i];
 1151                            endpoints[i] = endpoints[r];
 1152                            endpoints[r] = tmp;
 1153                        }
 1154                    }
 1155                }
 1156                break;
 1157            }
 1158            case Ice.EndpointSelectionType.Ordered:
 1159            {
 1160                // Nothing to do.
 1161                break;
 1162            }
 1163            default:
 1164            {
 1165                Debug.Assert(false);
 1166                break;
 1167            }
 1168        }
 1169
 1170        var arr = new EndpointI[endpoints.Count];
 1171        endpoints.CopyTo(arr);
 1172        return arr;
 1173    }
 1174
 1175    private sealed class CreateConnectionCallback : OutgoingConnectionFactory.CreateConnectionCallback
 1176    {
 1177        internal CreateConnectionCallback(RoutableReference rr, EndpointI[] endpoints, GetConnectionCallback cb)
 1178        {
 1179            _rr = rr;
 1180            _endpoints = endpoints;
 1181            _callback = cb;
 1182        }
 1183
 1184        public void setConnection(Ice.ConnectionI connection, bool compress)
 1185        {
 1186            //
 1187            // If we have a router, set the object adapter for this router
 1188            // (if any) to the new connection, so that callbacks from the
 1189            // router can be received over this new connection.
 1190            //
 1191            if (_rr._routerInfo != null && _rr._routerInfo.getAdapter() != null)
 1192            {
 1193                connection.setAdapter(_rr._routerInfo.getAdapter());
 1194            }
 1195            _callback.setConnection(connection, compress);
 1196        }
 1197
 1198        public void setException(Ice.LocalException ex)
 1199        {
 1200            _exception ??= ex;
 1201
 1202            if (_endpoints == null || ++_i == _endpoints.Length)
 1203            {
 1204                _callback.setException(_exception);
 1205                return;
 1206            }
 1207
 1208            bool more = _i != _endpoints.Length - 1;
 1209            var endpointList = new List<EndpointI> { _endpoints[_i] };
 1210            _rr.getInstance().outgoingConnectionFactory().create(endpointList, more, this);
 1211        }
 1212
 1213        private readonly RoutableReference _rr;
 1214        private readonly EndpointI[] _endpoints;
 1215        private readonly GetConnectionCallback _callback;
 1216        private int _i;
 1217        private Ice.LocalException _exception;
 1218    }
 1219
 1220    protected void createConnection(EndpointI[] allEndpoints, GetConnectionCallback callback)
 1221    {
 1222        EndpointI[] endpoints = filterEndpoints(allEndpoints);
 1223        if (endpoints.Length == 0)
 1224        {
 1225            callback.setException(new NoEndpointException(new ObjectPrxHelper(this)));
 1226            return;
 1227        }
 1228
 1229        //
 1230        // Finally, create the connection.
 1231        //
 1232        OutgoingConnectionFactory factory = getInstance().outgoingConnectionFactory();
 1233        if (getCacheConnection() || endpoints.Length == 1)
 1234        {
 1235            //
 1236            // Get an existing connection or create one if there's no
 1237            // existing connection to one of the given endpoints.
 1238            //
 1239            factory.create(endpoints.ToList(), false, new CreateConnectionCallback(this, null, callback));
 1240        }
 1241        else
 1242        {
 1243            //
 1244            // Go through the list of endpoints and try to create the
 1245            // connection until it succeeds. This is different from just
 1246            // calling create() with the given endpoints since this might
 1247            // create a new connection even if there's an existing
 1248            // connection for one of the endpoints.
 1249            //
 1250
 1251            factory.create(
 1252                new List<EndpointI> { endpoints[0] },
 1253                true,
 1254                new CreateConnectionCallback(this, endpoints, callback));
 1255        }
 1256    }
 1257
 1258    // Sets or resets _batchRequestQueue based on _mode.
 1259    private void setBatchRequestQueue()
 1260    {
 1261        _batchRequestQueue =
 1262            isBatch ? new BatchRequestQueue(getInstance(), getMode() == Mode.ModeBatchDatagram) : null;
 1263    }
 1264
 1265    private static readonly EndpointI[] _emptyEndpoints = [];
 1266
 1267    private BatchRequestQueue _batchRequestQueue;
 1268
 1269    private EndpointI[] _endpoints;
 1270    private string _adapterId;
 1271    private LocatorInfo _locatorInfo; // Null if no locator is used.
 1272    private RouterInfo _routerInfo; // Null if no router is used.
 1273    private bool _collocationOptimized;
 1274    private bool _cacheConnection;
 1275    private Ice.EndpointSelectionType _endpointSelection;
 1276    private TimeSpan _locatorCacheTimeout;
 1277
 1278    private string _connectionId = "";
 1279}