/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.spi.impl.proxyservice.impl; import com.hazelcast.core.DistributedObject; import com.hazelcast.core.HazelcastInstanceNotActiveException; import com.hazelcast.spi.AbstractDistributedObject; import com.hazelcast.spi.EventRegistration; import com.hazelcast.spi.EventService; import com.hazelcast.spi.InitializingObject; import com.hazelcast.spi.RemoteService; import com.hazelcast.spi.impl.eventservice.InternalEventService; import com.hazelcast.util.EmptyStatement; import com.hazelcast.util.ExceptionUtil; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static com.hazelcast.core.DistributedObjectEvent.EventType.CREATED; import static com.hazelcast.core.DistributedObjectEvent.EventType.DESTROYED; /** * A ProxyRegistry contains all proxies for a given service. For example, it contains all proxies for the IMap. */ public final class ProxyRegistry { private final ProxyServiceImpl proxyService; private final String serviceName; private final RemoteService service; private final ConcurrentMap<String, DistributedObjectFuture> proxies = new ConcurrentHashMap<String, DistributedObjectFuture>(); ProxyRegistry(ProxyServiceImpl proxyService, String serviceName) { this.proxyService = proxyService; this.serviceName = serviceName; this.service = proxyService.nodeEngine.getService(serviceName); } /** * Returns the name of the service for this ProxyRegistry. * * @return The name of the service for this ProxyRegistry. */ public String getServiceName() { return serviceName; } /** * Returns the number of proxies for this ProxyRegistry. * * @return The number of proxies for this ProxyRegistry. */ public int getProxyCount() { return proxies.size(); } /** * Checks if the ProxyRegistry contains the given key. * * @param name name of the key * @return true if the ProxyRegistry contains the key, false otherwise. */ boolean contains(String name) { return proxies.containsKey(name); } public Collection<String> getDistributedObjectNames() { // todo: not happy with exposing an internal data-structure to the outside. return proxies.keySet(); } /** * Gets the ProxyInfo of all fully initialized proxies in this registry. * The result is written into 'result'. * * @param result The ProxyInfo of all proxies in this registry. */ public void getProxyInfos(Collection<ProxyInfo> result) { for (Map.Entry<String, DistributedObjectFuture> entry : proxies.entrySet()) { DistributedObjectFuture future = entry.getValue(); if (future.isSetAndInitialized()) { String proxyName = entry.getKey(); result.add(new ProxyInfo(serviceName, proxyName)); } } } /** * Gets the DistributedObjects in this registry. The result is written into 'result'. * * @param result The DistributedObjects in this registry. */ public void getDistributedObjects(Collection<DistributedObject> result) { Collection<DistributedObjectFuture> futures = proxies.values(); for (DistributedObjectFuture future : futures) { if (!future.isSetAndInitialized()) { continue; } try { DistributedObject object = future.get(); result.add(object); } catch (Throwable ignored) { // ignore if proxy creation failed EmptyStatement.ignore(ignored); } } } /** * Retrieves a DistributedObject proxy or creates it if it is not available. * DistributedObject will be initialized by calling {@link InitializingObject#initialize()}, * if it implements {@link InitializingObject}. * * @param name The name of the DistributedObject proxy object to retrieve or create. * @param publishEvent true if a DistributedObjectEvent should be fired. * @return The DistributedObject instance. */ public DistributedObject getOrCreateProxy(String name, boolean publishEvent) { DistributedObjectFuture proxyFuture = getOrCreateProxyFuture(name, publishEvent, true); return proxyFuture.get(); } /** * Retrieves a DistributedObjectFuture or creates it if it is not available. * If {@code initialize} is false and DistributedObject implements {@link InitializingObject}, * {@link InitializingObject#initialize()} will be called before {@link DistributedObjectFuture#get()} returns. * * @param name The name of the DistributedObject proxy object to retrieve or create. * @param publishEvent true if a DistributedObjectEvent should be fired. * @param initialize true if the DistributedObject proxy object should be initialized. */ public DistributedObjectFuture getOrCreateProxyFuture(String name, boolean publishEvent, boolean initialize) { DistributedObjectFuture proxyFuture = proxies.get(name); if (proxyFuture == null) { if (!proxyService.nodeEngine.isRunning()) { throw new HazelcastInstanceNotActiveException(); } proxyFuture = createProxy(name, publishEvent, initialize); if (proxyFuture == null) { return getOrCreateProxyFuture(name, publishEvent, initialize); } } return proxyFuture; } /** * Creates a DistributedObject proxy if it is not created yet * * @param name The name of the distributedObject proxy object. * @param publishEvent true if a DistributedObjectEvent should be fired. * @param initialize true if he DistributedObject proxy object should be initialized. * @return The DistributedObject instance if it is created by this method, null otherwise. */ public DistributedObjectFuture createProxy(String name, boolean publishEvent, boolean initialize) { if (proxies.containsKey(name)) { return null; } if (!proxyService.nodeEngine.isRunning()) { throw new HazelcastInstanceNotActiveException(); } DistributedObjectFuture proxyFuture = new DistributedObjectFuture(); if (proxies.putIfAbsent(name, proxyFuture) != null) { return null; } return doCreateProxy(name, publishEvent, initialize, proxyFuture); } private DistributedObjectFuture doCreateProxy(String name, boolean publishEvent, boolean initialize, DistributedObjectFuture proxyFuture) { DistributedObject proxy; try { proxy = service.createDistributedObject(name); if (initialize && proxy instanceof InitializingObject) { try { ((InitializingObject) proxy).initialize(); } catch (Exception e) { // log and throw exception to be handled in outer catch block proxyService.logger.warning("Error while initializing proxy: " + proxy, e); throw e; } } proxyFuture.set(proxy, initialize); } catch (Throwable e) { // proxy creation or initialization failed // deregister future to avoid infinite hang on future.get() proxyFuture.setError(e); proxies.remove(name); throw ExceptionUtil.rethrow(e); } InternalEventService eventService = proxyService.nodeEngine.getEventService(); ProxyEventProcessor callback = new ProxyEventProcessor(proxyService.listeners.values(), CREATED, serviceName, name, proxy); eventService.executeEventCallback(callback); if (publishEvent) { publish(new DistributedObjectEventPacket(CREATED, serviceName, name)); } return proxyFuture; } /** * Destroys a proxy. * * @param name The name of the proxy to destroy. * @param publishEvent true if this destroy should be published. */ void destroyProxy(String name, boolean publishEvent) { final DistributedObjectFuture proxyFuture = proxies.remove(name); if (proxyFuture == null) { return; } DistributedObject proxy; try { proxy = proxyFuture.get(); } catch (Throwable t) { proxyService.logger.warning("Cannot destroy proxy [" + serviceName + ":" + name + "], since its creation is failed with " + t.getClass().getName() + ": " + t.getMessage()); return; } InternalEventService eventService = proxyService.nodeEngine.getEventService(); ProxyEventProcessor callback = new ProxyEventProcessor(proxyService.listeners.values(), DESTROYED, serviceName, name, proxy); eventService.executeEventCallback(callback); if (publishEvent) { publish(new DistributedObjectEventPacket(DESTROYED, serviceName, name)); } } private void publish(DistributedObjectEventPacket event) { EventService eventService = proxyService.nodeEngine.getEventService(); Collection<EventRegistration> registrations = eventService.getRegistrations( ProxyServiceImpl.SERVICE_NAME, ProxyServiceImpl.SERVICE_NAME); eventService.publishRemoteEvent(ProxyServiceImpl.SERVICE_NAME, registrations, event, event.getName().hashCode()); } /** * Destroys this proxy registry. */ void destroy() { for (DistributedObjectFuture future : proxies.values()) { if (!future.isSetAndInitialized()) { continue; } DistributedObject distributedObject = future.get(); if (distributedObject instanceof AbstractDistributedObject) { ((AbstractDistributedObject) distributedObject).invalidate(); } } proxies.clear(); } }