/// <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()}.");
        }
Пример #4
0
        public ClientProxy RemoveProxy(string service, string id)
        {
            var         ns = new DistributedObjectInfo(service, id);
            ClientProxy removed;

            _proxies.TryRemove(ns, out removed);
            return(removed);
        }
Пример #5
0
        /// <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();
            }
        }
Пример #7
0
 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)}]");
        }
Пример #9
0
        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();
        }
Пример #11
0
        /// <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);
        }
Пример #12
0
        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);
        }
Пример #13
0
        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)));
        }
Пример #14
0
        /// <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));
        }
Пример #16
0
 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);
 }