/// <summary> /// Deals with an object being disposed. /// </summary> /// <param name="o">The object.</param> private void OnObjectDisposed(DistributedObjectBase o) { // simply disposing the distributed object removes it from the list var info = new DistributedObjectInfo(o.ServiceName, o.Name); _objects.TryRemove(info); }
public void DestroyProxy(ClientProxy clientProxy) { var ns = new DistributedObjectInfo(clientProxy.Name, clientProxy.ServiceName); if (_proxies.TryRemove(ns, out var registeredProxyLazy)) { ClientProxy registeredProxy = null; try { registeredProxy = registeredProxyLazy.Value; if (registeredProxy != null) { try { registeredProxy.DestroyLocally(); } finally { registeredProxy.DestroyRemotely(); } } } finally { if (clientProxy != registeredProxy) { // The given proxy is stale and was already destroyed, but the caller // may have allocated local resources in the context of this stale proxy // instance after it was destroyed, so we have to cleanup it locally one // more time to make sure there are no leaking local resources. clientProxy.DestroyLocally(); } } } }
/// <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()}."); }
public ClientProxy RemoveProxy(string service, string id) { var ns = new DistributedObjectInfo(service, id); ClientProxy removed; _proxies.TryRemove(ns, out removed); return(removed); }
/// <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."); }
public void DestroyProxyLocally(string service, string id) { var objectNamespace = new DistributedObjectInfo(service, id); if (_proxies.TryRemove(objectNamespace, out var lazy)) { var clientProxy = lazy.Value; clientProxy.DestroyLocally(); } }
private async ValueTask TryDispose(DistributedObjectInfo info, IAsyncDisposable o) { try { await o.DisposeAsync().CAF(); } catch (Exception e) { _logger.LogWarning(e, $"Failed to dispose distributed object '{info.ServiceName}/{info.Name}'."); } }
private ClientProxy GetOrCreateProxyInternal <T>(string service, string id, bool remote) { CheckNotNull(service, "Service name is required!"); CheckNotNull(id, "Object name is required!"); var requestedInterface = typeof(T); var ns = new DistributedObjectInfo(service, id); var lazy = _proxies.GetOrAdd(ns, new Lazy <ClientProxy>(() => { if (!_proxyFactories.TryGetValue(service, out var factory)) { new ArgumentException($"No factory registered for service: {service}"); } try { var _clientProxy = factory(id, requestedInterface); if (remote) { Initialize(_clientProxy); } _clientProxy.OnInitialize(); return(_clientProxy); } catch (Exception e) { throw ExceptionUtil.Rethrow(e); } }, LazyThreadSafetyMode.ExecutionAndPublication)); ClientProxy clientProxy; try { clientProxy = lazy.Value; } catch (Exception e) { _proxies.TryRemove(ns, out _); throw ExceptionUtil.Rethrow(e); } // only return the existing proxy, if the requested type args match var proxyInterface = clientProxy.GetType().GetInterface(requestedInterface.Name); var proxyArgs = proxyInterface.GetGenericArguments(); var requestedArgs = requestedInterface.GetGenericArguments(); if (proxyArgs.SequenceEqual(requestedArgs)) { // the proxy we found matches what we were looking for return(clientProxy); } //TODO implement support for multiple generic parameters throw new InvalidCastException( $"Distributed object is already created with incompatible types [{string.Join(", ", (object[]) proxyArgs)}]"); }
public void DistributedObjectInfoTest() { var x = new DistributedObjectInfo("serviceName", "name"); Console.WriteLine(x); Assert.That(x.ServiceName, Is.EqualTo("serviceName")); Assert.That(x.Name, Is.EqualTo("name")); Assert.That(x, Resolves.Equatable( new DistributedObjectInfo("serviceName", "name"), new DistributedObjectInfo("serviceName", "other"), new DistributedObjectInfo("other", "other"))); }
/// <summary> /// Destroys a distributed object. /// </summary> /// <param name="o">The distributed object.</param> /// <param name="cancellationToken">A cancellation token.</param> public async ValueTask DestroyAsync(IDistributedObject o, CancellationToken cancellationToken = default) { // try to get the object - and then, dispose it var info = new DistributedObjectInfo(o.ServiceName, o.Name); var attempt = await _objects.TryGetAndRemoveAsync(info).CfAwait(); if (attempt) { await TryDispose(attempt.Value).CfAwait(); } var ob = (DistributedObjectBase)o; // we *know* all our objects inherit from the base object await ob.DestroyingAsync().CfAwait(); await DestroyAsync(o.ServiceName, o.Name, cancellationToken).CfAwait(); }
/// <summary> /// Destroys a distributed object. /// </summary> /// <param name="serviceName">The service name.</param> /// <param name="name">The unique object name.</param> /// <param name="cancellationToken">A cancellation token.</param> public async ValueTask DestroyAsync(string serviceName, string name, CancellationToken cancellationToken = default) { // try to get the object - and then, dispose it var info = new DistributedObjectInfo(serviceName, name); var attempt = await _objects.TryGetAndRemoveAsync(info).CAF(); if (attempt) { await TryDispose(info, attempt.Value).CAF(); } // regardless of whether the object was known locally, destroy on server var clientMessage = ClientDestroyProxyCodec.EncodeRequest(name, serviceName); var responseMessage = await _cluster.Messaging.SendAsync(clientMessage, cancellationToken).CAF(); _ = ClientDestroyProxyCodec.DecodeResponse(responseMessage); }
public void DistributedObjectInfoTest() { var x = new DistributedObjectInfo("serviceName", "name"); Console.WriteLine(x); Assert.That(x.ServiceName, Is.EqualTo("serviceName")); Assert.That(x.Name, Is.EqualTo("name")); Assert.That(x, Ish.Equatable( new DistributedObjectInfo("serviceName", "name"), new DistributedObjectInfo("serviceName", "other"), new DistributedObjectInfo("other", "name"))); var hash = x.GetHashCode(); Assert.That(hash, Is.Not.Zero); }
public ClientProxy GetOrCreateProxy <T>(string service, string id) where T : IDistributedObject { var objectInfo = new DistributedObjectInfo(service, id); var requestedInterface = typeof(T); var clientProxy = _proxies.GetOrAdd(objectInfo, distributedObjectInfo => { // create a new proxy, which needs initialization on server. var proxy = InitProxyLocal(service, id, requestedInterface); InitializeWithRetry(proxy); return(proxy); }); // only return the existing proxy, if the requested type args match var proxyInterface = clientProxy.GetType().GetInterface(requestedInterface.Name); var proxyArgs = proxyInterface.GetGenericArguments(); var requestedArgs = requestedInterface.GetGenericArguments(); if (proxyArgs.SequenceEqual(requestedArgs)) { // the proxy we found matches what we were looking for return(clientProxy); } var isAssignable = true; for (int i = 0; i < proxyArgs.Length; i++) { if (!proxyArgs[i].IsAssignableFrom(requestedArgs[i])) { isAssignable = false; break; } } if (isAssignable) { _proxies.TryRemove(objectInfo, out clientProxy); return(GetOrCreateProxy <T>(service, id)); } throw new InvalidCastException(string.Format("Distributed object is already created with incompatible types [{0}]", string.Join(", ", (object[])proxyArgs))); }
/// <summary> /// Destroys a distributed object. /// </summary> /// <param name="o">The distributed object.</param> /// <param name="cancellationToken">A cancellation token.</param> public async ValueTask DestroyAsync(IDistributedObject o, CancellationToken cancellationToken = default) { // try to get the object - and then, dispose it var info = new DistributedObjectInfo(o.ServiceName, o.Name); var attempt = await _objects.TryGetAndRemoveAsync(info).CfAwait(); if (attempt) { await TryDispose(attempt.Value).CfAwait(); } var ob = (DistributedObjectBase)o; // we *know* all our objects inherit from the base object await ob.DestroyingAsync().CfAwait(); // regardless of whether the object was known locally, destroy on server var clientMessage = ClientDestroyProxyCodec.EncodeRequest(o.Name, o.ServiceName); var responseMessage = await _cluster.Messaging.SendAsync(clientMessage, cancellationToken).CfAwait(); _ = ClientDestroyProxyCodec.DecodeResponse(responseMessage); }
public Guid AddDistributedObjectListener(IDistributedObjectListener listener) { var listenerService = _client.ListenerService; var request = ClientAddDistributedObjectListenerCodec.EncodeRequest(listenerService.RegisterLocalOnly); void HandleDistributedObjectEvent(string name, string serviceName, string eventTypeName, Guid source) { var ns = new DistributedObjectInfo(serviceName, name); _proxies.TryGetValue(ns, out var lazyProxy); // ClientProxy proxy = future == null ? null : future.get(); var eventType = (DistributedObjectEvent.DistributedEventType)Enum.Parse(typeof(DistributedObjectEvent.DistributedEventType), eventTypeName); var _event = new DistributedObjectEvent(eventType, serviceName, name, lazyProxy.Value, source, this); switch (eventType) { case DistributedObjectEvent.DistributedEventType.CREATED: listener.DistributedObjectCreated(_event); break; case DistributedObjectEvent.DistributedEventType.DESTROYED: listener.DistributedObjectDestroyed(_event); break; default: Logger.Warning($"Undefined DistributedObjectListener event type received: {eventType} !!!"); break; } } void EventHandler(ClientMessage message) => ClientAddDistributedObjectListenerCodec.EventHandler.HandleEvent(message, HandleDistributedObjectEvent); return(listenerService.RegisterListener(request, m => ClientAddDistributedObjectListenerCodec.DecodeResponse(m).Response, ClientRemoveDistributedObjectListenerCodec.EncodeRequest, EventHandler)); }
protected bool Equals(DistributedObjectInfo other) { return(string.Equals(_name, other._name) && string.Equals(_serviceName, other._serviceName)); }
protected bool Equals(DistributedObjectInfo other) { return string.Equals(_name, other._name) && string.Equals(_serviceName, other._serviceName); }