private void InitializeOnServer(ClientProxy clientProxy) { var initializationTarget = FindNextAddressToCreateARequest(); var invocationTarget = initializationTarget; if (initializationTarget != null && _client.GetConnectionManager().GetConnection(initializationTarget) == null) { invocationTarget = _client.GetClientClusterService().GetOwnerConnectionAddress(); } if (invocationTarget == null) { throw new IOException("Not able to setup owner connection!"); } var request = ClientCreateProxyCodec.EncodeRequest(clientProxy.GetName(), clientProxy.GetServiceName(), initializationTarget); try { ThreadUtil.GetResult(_client.GetInvocationService().InvokeOnTarget(request, invocationTarget)); } catch (Exception e) { throw ExceptionUtil.Rethrow(e); } }
/// <summary> /// Gets or creates a distributed object. /// </summary> /// <typeparam name="T">The type of the distributed object.</typeparam> /// <typeparam name="TImpl">The type of the implementation.</typeparam> /// <param name="serviceName">The unique name of the service.</param> /// <param name="name">The unique name of the object.</param> /// <param name="remote">Whether to create the object remotely too.</param> /// <param name="factory">The object factory.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <returns>The distributed object.</returns> public async Task <T> GetOrCreateAsync <T, TImpl>( string serviceName, string name, bool remote, Func <string, DistributedObjectFactory, Cluster, SerializationService, ILoggerFactory, TImpl> factory, CancellationToken cancellationToken = default) where TImpl : DistributedObjectBase, T { if (_disposed == 1) { throw new ObjectDisposedException("DistributedObjectFactory"); } var info = new DistributedObjectInfo(serviceName, name); async ValueTask <DistributedObjectBase> CreateAsync(DistributedObjectInfo info2, CancellationToken token) { var x = factory(name, this, _cluster, _serializationService, _loggerFactory); x.ObjectDisposed = OnObjectDisposed; // this is why is has to be DistributedObjectBase // initialize the object if (remote) { var requestMessage = ClientCreateProxyCodec.EncodeRequest(x.Name, x.ServiceName); _ = await _cluster.Messaging.SendAsync(requestMessage, token).CfAwait(); } x.OnInitialized(); _logger.LogDebug("Initialized ({Object}) distributed object.", info2); return(x); } // try to get the object - thanks to the concurrent dictionary there will be only 1 task // and if several concurrent requests are made, they will all await that same task var o = await _objects.GetOrAddAsync(info, CreateAsync, cancellationToken).CfAwait(); // race condition: maybe the factory has been disposed and is already disposing // objects and will ignore this new object even though it has been added to the // dictionary, so take care of it ourselves if (_disposed == 1) { await o.DisposeAsync().CfAwait(); throw new ObjectDisposedException("DistributedObjectFactory"); } // if the object is a T then we can return it if (o is T t) { return(t); } // otherwise, the client was already used to retrieve an object with the specified service // name and object name, but a different type, for instance IHList<int> vs IHList<string>, // and we just cannot support this = throw throw new HazelcastException($"A distributed object with the specified service name ({serviceName}) " + $"and object name ({name}) exists but of type {o.GetType().ToCsString()}, " + $"instead of {typeof(T).ToCsString()}."); }
/// <summary> /// Gets or creates a distributed object. /// </summary> /// <typeparam name="T">The type of the distributed object.</typeparam> /// <typeparam name="TImpl">The type of the implementation.</typeparam> /// <param name="serviceName">The unique name of the service.</param> /// <param name="name">The unique name of the object.</param> /// <param name="remote">Whether to create the object remotely too.</param> /// <param name="factory">The object factory.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <returns>The distributed object.</returns> public async Task <T> GetOrCreateAsync <T, TImpl>( string serviceName, string name, bool remote, Func <string, DistributedObjectFactory, Cluster, ISerializationService, ILoggerFactory, TImpl> factory, CancellationToken cancellationToken = default) where TImpl : DistributedObjectBase, T { if (_disposed == 1) { throw new ObjectDisposedException("DistributedObjectFactory"); } await _cluster.ThrowIfNotConnected().CAF(); var k = new DistributedObjectInfo(serviceName, name); async ValueTask <DistributedObjectBase> CreateAsync(DistributedObjectInfo info, CancellationToken token) { var x = factory(name, this, _cluster, _serializationService, _loggerFactory); x.OnDispose = ObjectDisposed; // this is why is has to be DistributedObjectBase // initialize the object if (remote) { var requestMessage = ClientCreateProxyCodec.EncodeRequest(x.Name, x.ServiceName); _ = await _cluster.Messaging.SendAsync(requestMessage, token).CAF(); } x.OnInitialized(); _logger.LogDebug("Initialized '{ServiceName}/{Name}' distributed object.", info.ServiceName, info.Name); return(x); } // try to get the object - thanks to the concurrent dictionary there will be only 1 task // and if several concurrent requests are made, they will all await that same task var o = await _objects.GetOrAddAsync(k, CreateAsync, cancellationToken).CAF(); // race condition: maybe the factory has been disposed and is already disposing // objects and will ignore this new object even though it has been added to the // dictionary, so take care of it ourselves if (_disposed == 1) { await o.DisposeAsync().CAF(); throw new ObjectDisposedException("DistributedObjectFactory"); } if (o is T t) { return(t); } // if the object that was retrieved is not of the right type, it's a problem // preserve the existing object, but throw throw new InvalidCastException("A distributed object with the specified service name and name, but " + "with a different type, has already been created."); }
/// <summary> /// Creates all known <see cref="IDistributedObject"/> on a cluster. /// </summary> /// <returns>A task that will complete when the state has been sent.</returns> /// <remarks> /// <para>This is used when connecting to a new cluster.</para> /// </remarks> public async ValueTask CreateAllAsync(CancellationToken cancellationToken) { await foreach (var(key, _) in _objects) { // if the cluster goes down, we want to stop everything // but each invocation is non-cancellable cancellationToken.ThrowIfCancellationRequested(); try { var requestMessage = ClientCreateProxyCodec.EncodeRequest(key.Name, key.ServiceName); await _cluster.Messaging.SendAsync(requestMessage, cancellationToken).CfAwait(); } catch (Exception e) { _logger.LogError(e, $"Failed to create ({key}) distributed object on new cluster."); } } }
/// <summary> /// Creates all known <see cref="IDistributedObject"/> on a cluster. /// </summary> /// <returns>A task that will complete when the state has been sent.</returns> /// <remarks> /// <para>This is used when connecting to a new cluster.</para> /// </remarks> public async ValueTask CreateAllAsync(MemberConnection connection) { await foreach (var(key, _) in _objects) { // if the connection goes down, stop if (!connection.Active) { return; } try { var requestMessage = ClientCreateProxyCodec.EncodeRequest(key.Name, key.ServiceName); await _cluster.Messaging.SendToMemberAsync(requestMessage, connection).CfAwait(); } catch (Exception e) { _logger.LogError(e, $"Failed to create ({key}) distributed object on new cluster."); } } }
private void Initialize(ClientProxy clientProxy) { var request = ClientCreateProxyCodec.EncodeRequest(clientProxy.Name, clientProxy.ServiceName); ThreadUtil.GetResult(_client.InvocationService.InvokeOnRandomTarget(request)); }