< Summary

Information
Class: Ice.Communicator
Assembly: Ice
File(s): /_/csharp/src/Ice/Communicator.cs
Tag: 91_21789722663
Line coverage
92%
Covered lines: 85
Uncovered lines: 7
Coverable lines: 92
Total lines: 492
Line coverage: 92.3%
Branch coverage
85%
Covered branches: 12
Total branches: 14
Branch coverage: 85.7%
Method coverage
92%
Covered methods: 35
Total methods: 38
Method coverage: 92.1%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_shutdownCompleted()100%11100%
get_instance()100%11100%
.ctor(...)100%22100%
.ctor(...)100%210%
Dispose()100%11100%
DisposeAsync()100%11100%
destroy()100%11100%
shutdown()100%11100%
waitForShutdown()100%11100%
isShutdown()100%11100%
stringToProxy(...)100%22100%
proxyToString(...)50%22100%
propertyToProxy(...)100%22100%
proxyToProperty(...)100%11100%
identityToString(...)100%11100%
createObjectAdapter(...)100%11100%
createObjectAdapterWithEndpoints(...)50%2.06275%
createObjectAdapterWithRouter(...)100%44100%
getDefaultObjectAdapter()100%11100%
setDefaultObjectAdapter(...)100%11100%
getImplicitContext()100%11100%
getProperties()100%11100%
getLogger()100%11100%
addSliceLoader(...)100%210%
getObserver()100%210%
getDefaultRouter()100%11100%
setDefaultRouter(...)100%11100%
getDefaultLocator()100%11100%
setDefaultLocator(...)100%11100%
getPluginManager()100%11100%
flushBatchRequests(...)100%1.02175%
flushBatchRequestsAsync(...)100%11100%
createAdmin(...)100%11100%
getAdmin()100%11100%
addAdminFacet(...)100%11100%
removeAdminFacet(...)100%11100%
findAdminFacet(...)100%11100%
findAllAdminFacets()100%11100%

File(s)

/_/csharp/src/Ice/Communicator.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3#nullable enable
 4
 5using Ice.Internal;
 6using System.Net.Security;
 7
 8namespace Ice;
 9
 10/// <summary>
 11/// Communicator is the central object in Ice. Its responsibilities include:
 12/// <list type="bullet">
 13/// <item>creating and managing outgoing connections</item>
 14/// <item>executing callbacks in its client thread pool</item>
 15/// <item>creating and destroying object adapters</item>
 16/// <item>loading plug-ins</item>
 17/// <item>managing properties (configuration), retries, logging, instrumentation, and more.</item>
 18/// </list>
 19/// A Communicator is usually the first object you create when programming with Ice.
 20/// You can create multiple communicators in a single program, but this is not common.
 21/// </summary>
 22/// <seealso cref="Logger"/>
 23/// <seealso cref="ObjectAdapter"/>
 24/// <seealso cref="Properties"/>
 25public sealed class Communicator : IDisposable, IAsyncDisposable
 26{
 27    /// <summary>
 28    /// Gets a task that completes when the communicator's shutdown completes. This task always completes successfully.
 29    /// </summary>
 30    /// <remarks>The shutdown of a communicator completes when all its incoming connections are closed. Awaiting this
 31    /// task is equivalent to calling <see cref="waitForShutdown" />.</remarks>
 32    /// <seealso cref="shutdown" />
 33    public Task shutdownCompleted
 34    {
 35        get
 36        {
 37            // It would be much nicer to wait asynchronously but doing so requires significant refactoring.
 138            var tcs = new TaskCompletionSource(); // created "on demand", when the user calls shutdownCompleted
 139            _ = Task.Run(() =>
 140            {
 141                waitForShutdown();
 142                tcs.SetResult();
 143            });
 144            return tcs.Task;
 45        }
 46    }
 47
 148    internal Instance instance { get; }
 49
 50    private const string _flushBatchRequests_name = "flushBatchRequests";
 51
 52    /// <summary>
 53    /// Initializes a new instance of the <see cref="Communicator" /> class.
 54    /// </summary>
 55    /// <param name="initData">Options for the new communicator.</param>
 156    public Communicator(InitializationData? initData = null)
 57    {
 158        initData = initData is null ? new InitializationData() : initData with { };
 59
 160        instance = new Instance();
 161        instance.initialize(this, initData);
 62
 63        try
 64        {
 165            instance.finishSetup(this);
 166        }
 167        catch
 68        {
 169            instance.destroy();
 170            throw;
 71        }
 172    }
 73
 74    /// <summary>
 75    /// Initializes a new instance of the <see cref="Communicator" /> class, using Ice properties parsed from
 76    /// command-line arguments. This constructor uses <paramref name="args"/> to create the <see cref="Properties"/>
 77    /// of the new communicator.
 78    /// </summary>
 79    /// <param name="args">The command-line arguments.</param>
 80    public Communicator(ref string[] args)
 081        : this(new InitializationData { properties = new Properties(ref args) })
 82    {
 083    }
 84
 85    /// <summary>
 86    /// Disposes this communicator. This method calls <see cref="shutdown" /> implicitly. Calling dispose destroys all
 87    /// object adapters, and closes all outgoing connections. This method waits for all outstanding dispatches to
 88    /// complete before returning. This includes "bidirectional dispatches" that execute on outgoing connections.
 89    /// </summary>
 190    public void Dispose() => destroy();
 91
 92    /// <summary>
 93    /// Disposes this communicator asynchronously. Like <see cref="Communicator.shutdownCompleted" />, this method
 94    /// waits for all outstanding dispatches to complete.
 95    /// </summary>
 96    /// <returns>A task that completes when the communicator is disposed.</returns>
 97    public ValueTask DisposeAsync()
 98    {
 99        // A truly async implementation would be nicer but requires significant refactoring.
 1100        var tcs = new TaskCompletionSource();
 1101        _ = Task.Run(() =>
 1102        {
 1103            Dispose(); // can block for a while
 1104            tcs.SetResult();
 1105        });
 106
 1107        return new(tcs.Task);
 108    }
 109
 110    /// <summary>
 111    /// Destroys this communicator. It's an alias for <see cref="Dispose"/>.
 112    /// </summary>
 1113    public void destroy() => instance.destroy();
 114
 115    /// <summary>
 116    /// Shuts down this communicator. This method calls <see cref="ObjectAdapter.deactivate"/> on all object adapters
 117    /// created by this communicator. Shutting down a communicator has no effect on outgoing connections.
 118    /// </summary>
 119    /// <seealso cref="waitForShutdown" />
 120    public void shutdown()
 121    {
 122        try
 123        {
 1124            instance.objectAdapterFactory().shutdown();
 1125        }
 1126        catch (CommunicatorDestroyedException)
 127        {
 128            // Ignore
 1129        }
 1130    }
 131
 132    /// <summary>
 133    /// Waits for the shutdown of this communicator to complete.
 134    /// This method calls <see cref="ObjectAdapter.waitForDeactivate" /> on all object adapters created by this
 135    /// communicator. In a client application that does not accept incoming connections, this method returns as soon as
 136    /// another thread calls <see cref="shutdown" /> or <see cref="Dispose" /> on this communicator.
 137    /// </summary>
 138    public void waitForShutdown()
 139    {
 140        try
 141        {
 1142            instance.objectAdapterFactory().waitForShutdown();
 1143        }
 1144        catch (CommunicatorDestroyedException)
 145        {
 146            // Ignore
 1147        }
 1148    }
 149
 150    /// <summary>
 151    /// Checks whether or not <see cref="shutdown" /> was called on this communicator.
 152    /// </summary>
 153    /// <returns><see langword="true"/> if shutdown was called on this communicator;
 154    /// <see langword="false"/> otherwise.</returns>
 155    public bool isShutdown()
 156    {
 157        try
 158        {
 1159            return instance.objectAdapterFactory().isShutdown();
 160        }
 1161        catch (CommunicatorDestroyedException)
 162        {
 1163            return true;
 164        }
 1165    }
 166
 167    /// <summary>
 168    /// Converts a stringified proxy into a proxy.
 169    /// </summary>
 170    /// <param name="str">The stringified proxy to convert into a proxy.</param>
 171    /// <returns>The proxy, or null if <paramref name="str" /> is an empty string.</returns>
 172    /// <exception cref="ParseException">Thrown when <paramref name="str" /> is not a valid proxy string.</exception>
 173    public ObjectPrx? stringToProxy(string str)
 174    {
 1175        Reference? reference = instance.referenceFactory().create(str, "");
 1176        return reference is not null ? new ObjectPrxHelper(reference) : null;
 177    }
 178
 179    /// <summary>
 180    /// Converts a proxy into a string.
 181    /// </summary>
 182    /// <param name="proxy">The proxy to convert into a stringified proxy.</param>
 183    /// <returns>The stringified proxy, or an empty string if <paramref name="proxy" /> is null.</returns>
 184    public string proxyToString(ObjectPrx? proxy) =>
 1185        proxy is null ? "" : ((ObjectPrxHelperBase)proxy).iceReference().ToString();
 186
 187    /// <summary>
 188    /// Converts a set of proxy properties into a proxy. The "base" name supplied in the <paramref name="property" />
 189    /// argument refers to a property containing a stringified proxy, such as `MyProxy=id:tcp -h localhost -p 10000`.
 190    /// Additional properties configure local settings for the proxy.
 191    /// </summary>
 192    /// <param name="property">The base property name.</param>
 193    /// <returns>The proxy, or <c>null</c> if the property is not set.</returns>
 194    public ObjectPrx? propertyToProxy(string property)
 195    {
 1196        string proxy = instance.initializationData().properties!.getProperty(property);
 1197        Reference? reference = instance.referenceFactory().create(proxy, property);
 1198        return reference is not null ? new ObjectPrxHelper(reference) : null;
 199    }
 200
 201    /// <summary>
 202    /// Converts a proxy into a set of proxy properties.
 203    /// </summary>
 204    /// <param name="proxy">The proxy.</param>
 205    /// <param name="prefix">The base property name.</param>
 206    /// <returns>The property set.</returns>
 207    public Dictionary<string, string> proxyToProperty(ObjectPrx proxy, string prefix) =>
 1208        ((ObjectPrxHelperBase)proxy).iceReference().toProperty(prefix);
 209
 210    /// <summary>
 211    /// Converts an identity into a string.
 212    /// </summary>
 213    /// <param name="ident">The identity to convert into a string.</param>
 214    /// <returns>The "stringified" identity.</returns>
 1215    public string identityToString(Identity ident) => Util.identityToString(ident, instance.toStringMode());
 216
 217    /// <summary>
 218    /// Creates a new object adapter. The endpoints for the object adapter are taken from the property
 219    /// <c>name.Endpoints</c>.
 220    /// It is legal to create an object adapter with the empty string as its name. Such an object
 221    /// adapter is accessible via bidirectional connections or by collocated invocations.
 222    /// </summary>
 223    /// <param name="name">The object adapter name.</param>
 224    /// <param name="serverAuthenticationOptions">The SSL options for server connections.</param>
 225    /// <returns>The new object adapter.</returns>
 226    /// <exception cref="InitializationException">Thrown when a named object adapter is created for which no
 227    /// configuration can be found.</exception>
 228    public ObjectAdapter createObjectAdapter(
 229        string name,
 230        SslServerAuthenticationOptions? serverAuthenticationOptions = null) =>
 1231        instance.objectAdapterFactory().createObjectAdapter(name, null, serverAuthenticationOptions);
 232
 233    /// <summary>
 234    /// Creates a new object adapter with endpoints. This method sets the property <c>name.Endpoints</c>,
 235    /// and then calls <see cref="createObjectAdapter"/>. It is provided as a convenience method.
 236    /// Calling this method with an empty name will result in a UUID being generated for the name.
 237    /// </summary>
 238    /// <param name="name">The object adapter name.</param>
 239    /// <param name="endpoints">The endpoints of the object adapter.</param>
 240    /// <param name="serverAuthenticationOptions">The SSL options for server connections.</param>
 241    /// <returns>The new object adapter.</returns>
 242    public ObjectAdapter createObjectAdapterWithEndpoints(
 243        string name,
 244        string endpoints,
 245        SslServerAuthenticationOptions? serverAuthenticationOptions = null)
 246    {
 1247        if (name.Length == 0)
 248        {
 0249            name = Guid.NewGuid().ToString();
 250        }
 251
 1252        getProperties().setProperty(name + ".Endpoints", endpoints);
 1253        return instance.objectAdapterFactory().createObjectAdapter(name, null, serverAuthenticationOptions);
 254    }
 255
 256    /// <summary>
 257    /// Creates a new object adapter with a router.
 258    /// This method creates a routed object adapter. Calling this method with an empty name will result in a UUID
 259    /// being generated for the name.
 260    /// </summary>
 261    /// <param name="name">The object adapter name.</param>
 262    /// <param name="router">The router.</param>
 263    /// <returns>The new object adapter.</returns>
 264    public ObjectAdapter createObjectAdapterWithRouter(string name, RouterPrx router)
 265    {
 1266        if (name.Length == 0)
 267        {
 1268            name = Guid.NewGuid().ToString();
 269        }
 270
 271        //
 272        // We set the proxy properties here, although we still use the proxy supplied.
 273        //
 1274        Dictionary<string, string> properties = proxyToProperty(router, name + ".Router");
 1275        foreach (KeyValuePair<string, string> entry in properties)
 276        {
 1277            getProperties().setProperty(entry.Key, entry.Value);
 278        }
 279
 1280        return instance.objectAdapterFactory().createObjectAdapter(name, router, serverAuthenticationOptions: null);
 281    }
 282
 283    /// <summary>
 284    /// Gets the object adapter that is associated by default with new outgoing connections created by this
 285    /// communicator. This method returns null unless you set a non-null default object adapter using
 286    /// <see cref="setDefaultObjectAdapter" />.
 287    /// </summary>
 288    /// <returns>The object adapter associated by default with new outgoing connections.</returns>
 289    /// <seealso cref="Connection.getAdapter" />
 1290    public ObjectAdapter? getDefaultObjectAdapter() => instance.outgoingConnectionFactory().getDefaultObjectAdapter();
 291
 292    /// <summary>
 293    /// Sets the object adapter that will be associated with new outgoing connections created by this communicator. This
 294    /// method has no effect on existing outgoing connections, or on incoming connections.
 295    /// </summary>
 296    /// <param name="adapter">The object adapter to associate with new outgoing connections.</param>
 297    /// <seealso cref="Connection.setAdapter" />
 298    public void setDefaultObjectAdapter(ObjectAdapter? adapter) =>
 1299        instance.outgoingConnectionFactory().setDefaultObjectAdapter(adapter);
 300
 301    /// <summary>
 302    /// Gets the implicit context associated with this communicator.
 303    /// </summary>
 304    /// <returns>The implicit context associated with this communicator; returns null when the property
 305    /// Ice.ImplicitContext is not set or is set to None.</returns>
 1306    public ImplicitContext getImplicitContext() => instance.getImplicitContext();
 307
 308    /// <summary>
 309    /// Gets the properties for this communicator.
 310    /// </summary>
 311    /// <returns>This communicator's properties.</returns>
 1312    public Properties getProperties() => instance.initializationData().properties!;
 313
 314    /// <summary>
 315    /// Gets the logger for this communicator.
 316    /// </summary>
 317    /// <returns>This communicator's logger.</returns>
 1318    public Logger getLogger() => instance.initializationData().logger!;
 319
 320    /// <summary>
 321    /// Adds a Slice loader to this communicator, after the Slice loader set in <see cref="InitializationData" />(if
 322    /// any) and after other Slice loaders added by this method.
 323    /// </summary>
 324    /// <param name="loader"> The Slice loader to add.</param>
 325    /// <remarks>This method is not thread-safe and should only be called right after the communicator is created.
 326    /// It's provided for applications that cannot set the Slice loader in the <see cref="InitializationData" /> of the
 327    /// communicator, such as IceBox services.</remarks>
 0328    public void addSliceLoader(SliceLoader loader) => instance.addSliceLoader(loader);
 329
 330    /// <summary>
 331    /// Gets the observer resolver object for this communicator.
 332    /// </summary>
 333    /// <returns>This communicator's observer resolver object.</returns>
 0334    public Instrumentation.CommunicatorObserver? getObserver() => instance.initializationData().observer;
 335
 336    /// <summary>
 337    /// Gets the default router for this communicator.
 338    /// </summary>
 339    /// <returns>The default router for this communicator.</returns>
 1340    public RouterPrx? getDefaultRouter() => instance.referenceFactory().getDefaultRouter();
 341
 342    /// <summary>
 343    /// Sets a default router for this communicator.
 344    /// All newly created proxies will use this default router. To disable the default router, null can be used. Note
 345    /// that this method has no effect on existing proxies. You can also set a router for an individual proxy by calling
 346    /// <see cref="ObjectPrx.ice_router(RouterPrx?)" /> on the proxy.
 347    /// </summary>
 348    /// <param name="router">The default router to use for this communicator.</param>
 1349    public void setDefaultRouter(RouterPrx? router) => instance.setDefaultRouter(router);
 350
 351    /// <summary>
 352    /// Gets the default locator for this communicator.
 353    /// </summary>
 354    /// <returns>The default locator for this communicator.</returns>
 1355    public LocatorPrx? getDefaultLocator() => instance.referenceFactory().getDefaultLocator();
 356
 357    /// <summary>
 358    /// Sets a default Ice locator for this communicator.
 359    /// All newly created proxy and object adapters will use this default locator. To disable the default locator, null
 360    /// can be used. Note that this method has no effect on existing proxies or object adapters.
 361    /// You can also set a locator for an individual proxy by calling <see cref="ObjectPrx.ice_locator(LocatorPrx?)" />
 362    /// on the proxy, or for an object adapter by calling <see cref="ObjectAdapter.setLocator(LocatorPrx)" /> on the
 363    /// object adapter.
 364    /// </summary>
 365    /// <param name="locator">The default locator to use for this communicator.</param>
 1366    public void setDefaultLocator(LocatorPrx? locator) => instance.setDefaultLocator(locator);
 367
 368    /// <summary>
 369    /// Gets the plug-in manager for this communicator.
 370    /// </summary>
 371    /// <returns>This communicator's plug-in manager.</returns>
 1372    public PluginManager getPluginManager() => instance.pluginManager();
 373
 374    /// <summary>
 375    /// Flushes any pending batch requests for this communicator.
 376    /// This means all batch requests invoked on fixed proxies for all connections associated with the communicator.
 377    /// Any errors that occur while flushing a connection are ignored.
 378    /// </summary>
 379    /// <param name="compress">Specifies whether or not the queued batch requests should be compressed before being sent
 380    /// over the wire.</param>
 381    public void flushBatchRequests(CompressBatch compress)
 382    {
 383        try
 384        {
 1385            var completed = new FlushBatchTaskCompletionCallback();
 1386            var outgoing = new CommunicatorFlushBatchAsync(instance, completed);
 1387            outgoing.invoke(_flushBatchRequests_name, compress, true);
 1388            completed.Task.Wait();
 1389        }
 0390        catch (AggregateException ex)
 391        {
 0392            throw ex.InnerException!;
 393        }
 1394    }
 395
 396    public Task flushBatchRequestsAsync(
 397        CompressBatch compress,
 398        IProgress<bool>? progress = null,
 399        CancellationToken cancel = default)
 400    {
 1401        var completed = new FlushBatchTaskCompletionCallback(progress, cancel);
 1402        var outgoing = new CommunicatorFlushBatchAsync(instance, completed);
 1403        outgoing.invoke(_flushBatchRequests_name, compress, false);
 1404        return completed.Task;
 405    }
 406
 407    /// <summary>
 408    /// Adds the Admin object with all its facets to the provided object adapter.
 409    /// If Ice.Admin.ServerId is set and the provided object adapter has a Locator, createAdmin registers the Admin's
 410    /// Process facet with the Locator's LocatorRegistry. createAdmin must only be called once; subsequent calls raise
 411    /// InitializationException.
 412    /// </summary>
 413    /// <param name="adminAdapter">The object adapter used to host the Admin object; if null and Ice.Admin.Endpoints is
 414    /// set, create, activate and use the Ice.Admin object adapter.</param>
 415    /// <param name="adminId">The identity of the Admin object.</param>
 416    /// <returns>A proxy to the main ("") facet of the Admin object.</returns>
 417    public ObjectPrx createAdmin(ObjectAdapter adminAdapter, Identity adminId) =>
 1418        instance.createAdmin(adminAdapter, adminId);
 419
 420    /// <summary>
 421    /// Gets a proxy to the main facet of the Admin object.
 422    /// getAdmin also creates the Admin object and creates and activates the Ice.Admin object adapter to host this
 423    /// Admin object if Ice.Admin.Endpoints is set. The identity of the Admin object created by getAdmin is
 424    /// {value of Ice.Admin.InstanceName}/admin, or {UUID}/admin when  Ice.Admin.InstanceName is not set. If
 425    /// Ice.Admin.DelayCreation is 0 or not set, getAdmin is called  by the communicator initialization, after
 426    /// initialization of all plugins.
 427    /// </summary>
 428    /// <returns>A proxy to the main ("") facet of the Admin object, or a null proxy if no Admin object is configured.
 429    /// </returns>
 1430    public ObjectPrx? getAdmin() => instance.getAdmin();
 431
 432    /// <summary>
 433    /// Adds a new facet to the Admin object.
 434    /// Adding a servant with a facet that is already registered throws AlreadyRegisteredException.
 435    /// </summary>
 436    /// <param name="servant">The servant that implements the new Admin facet.</param>
 437    /// <param name="facet">The name of the new Admin facet.</param>
 1438    public void addAdminFacet(Object servant, string facet) => instance.addAdminFacet(servant, facet);
 439
 440    /// <summary>
 441    /// Removes the following facet to the Admin object.
 442    /// Removing a facet that was not previously registered throws <see cref="NotRegisteredException" />.
 443    /// </summary>
 444    /// <param name="facet">The name of the Admin facet.</param>
 445    /// <returns>The servant associated with this Admin facet.</returns>
 1446    public Object removeAdminFacet(string facet) => instance.removeAdminFacet(facet);
 447
 448    /// <summary>
 449    /// Returns a facet of the Admin object.
 450    /// </summary>
 451    /// <param name="facet">The name of the Admin facet.</param>
 452    /// <returns>The servant associated with this Admin facet, or null if no facet is registered with the given name.
 453    /// </returns>
 1454    public Object? findAdminFacet(string facet) => instance.findAdminFacet(facet);
 455
 456    /// <summary>
 457    /// Returns a map of all facets of the Admin object.
 458    /// </summary>
 459    /// <returns>A collection containing all the facet names and servants of the Admin object.</returns>
 1460    public Dictionary<string, Object> findAllAdminFacets() => instance.findAllAdminFacets();
 461}
 462
 463/// <summary>
 464/// The output mode for xxxToString methods such as identityToString and proxyToString.
 465/// The actual encoding format for
 466/// the string is the same for all modes: you don't need to specify an encoding format or mode when reading such a
 467/// string.
 468/// </summary>
 469public enum ToStringMode
 470{
 471    /// <summary>
 472    /// Characters with ordinal values greater than 127 are kept as-is in the resulting string.
 473    /// Non-printable ASCII
 474    /// characters with ordinal values 127 and below are encoded as \\t, \\n (etc.) or \\unnnn.
 475    /// </summary>
 476    Unicode,
 477
 478    /// <summary>
 479    /// Characters with ordinal values greater than 127 are encoded as universal character names in the resulting
 480    /// string: \\unnnn for BMP characters and \\Unnnnnnnn for non-BMP characters.
 481    /// Non-printable ASCII characters
 482    /// with ordinal values 127 and below are encoded as \\t, \\n (etc.) or \\unnnn.
 483    /// </summary>
 484    ASCII,
 485
 486    /// <summary>
 487    /// Characters with ordinal values greater than 127 are encoded as a sequence of UTF-8 bytes using octal escapes.
 488    /// Characters with ordinal values 127 and below are encoded as \\t, \\n (etc.) or an octal escape. Use this mode
 489    /// to generate strings compatible with Ice 3.6 and earlier.
 490    /// </summary>
 491    Compat
 492}