/*
* 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.DistributedObjectListener;
import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.core.Member;
import com.hazelcast.internal.metrics.MetricsProvider;
import com.hazelcast.internal.metrics.MetricsRegistry;
import com.hazelcast.internal.metrics.Probe;
import com.hazelcast.internal.util.counters.MwCounter;
import com.hazelcast.logging.ILogger;
import com.hazelcast.spi.EventPublishingService;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.OperationService;
import com.hazelcast.spi.PostJoinAwareService;
import com.hazelcast.spi.ProxyService;
import com.hazelcast.spi.RemoteService;
import com.hazelcast.spi.exception.DistributedObjectDestroyedException;
import com.hazelcast.spi.impl.NodeEngineImpl;
import com.hazelcast.spi.impl.proxyservice.InternalProxyService;
import com.hazelcast.spi.impl.proxyservice.impl.operations.DistributedObjectDestroyOperation;
import com.hazelcast.spi.impl.proxyservice.impl.operations.PostJoinProxyOperation;
import com.hazelcast.util.ConstructorFunction;
import com.hazelcast.util.EmptyStatement;
import com.hazelcast.util.FutureUtil.ExceptionHandler;
import com.hazelcast.util.UuidUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import static com.hazelcast.core.DistributedObjectEvent.EventType.CREATED;
import static com.hazelcast.internal.metrics.ProbeLevel.MANDATORY;
import static com.hazelcast.internal.util.counters.MwCounter.newMwCounter;
import static com.hazelcast.util.ConcurrencyUtil.getOrPutIfAbsent;
import static com.hazelcast.util.ExceptionUtil.peel;
import static com.hazelcast.util.FutureUtil.waitWithDeadline;
import static com.hazelcast.util.Preconditions.checkNotNull;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.WARNING;
public class ProxyServiceImpl
implements InternalProxyService, PostJoinAwareService,
EventPublishingService<DistributedObjectEventPacket, Object>, MetricsProvider {
public static final String SERVICE_NAME = "hz:core:proxyService";
private static final int TRY_COUNT = 10;
private static final long DESTROY_TIMEOUT_SECONDS = 30;
final NodeEngineImpl nodeEngine;
final ILogger logger;
final ConcurrentMap<String, DistributedObjectListener> listeners =
new ConcurrentHashMap<String, DistributedObjectListener>();
private final ConstructorFunction<String, ProxyRegistry> registryConstructor =
new ConstructorFunction<String, ProxyRegistry>() {
public ProxyRegistry createNew(String serviceName) {
return new ProxyRegistry(ProxyServiceImpl.this, serviceName);
}
};
private final ConcurrentMap<String, ProxyRegistry> registries =
new ConcurrentHashMap<String, ProxyRegistry>();
@Probe(name = "createdCount", level = MANDATORY)
private final MwCounter createdCounter = newMwCounter();
@Probe(name = "destroyedCount", level = MANDATORY)
private final MwCounter destroyedCounter = newMwCounter();
private final ExceptionHandler destroyProxyExceptionHandler = new ExceptionHandler() {
@Override
public void handleException(Throwable throwable) {
boolean causedByInactiveInstance = peel(throwable) instanceof HazelcastInstanceNotActiveException;
Level level = causedByInactiveInstance ? FINEST : WARNING;
logger.log(level, "Error while destroying a proxy.", throwable);
}
};
public ProxyServiceImpl(NodeEngineImpl nodeEngine) {
this.nodeEngine = nodeEngine;
this.logger = nodeEngine.getLogger(ProxyService.class.getName());
}
@Override
public void provideMetrics(MetricsRegistry registry) {
registry.scanAndRegister(this, "proxy");
}
public void init() {
nodeEngine.getEventService().registerListener(SERVICE_NAME, SERVICE_NAME, new Object());
}
@Probe(name = "proxyCount")
@Override
public int getProxyCount() {
int count = 0;
for (ProxyRegistry registry : registries.values()) {
count += registry.getProxyCount();
}
return count;
}
@Override
public void initializeDistributedObject(String serviceName, String name) {
checkServiceNameNotNull(serviceName);
checkObjectNameNotNull(name);
ProxyRegistry registry = getOrCreateRegistry(serviceName);
registry.createProxy(name, true, true);
createdCounter.inc();
}
public ProxyRegistry getOrCreateRegistry(String serviceName) {
return getOrPutIfAbsent(registries, serviceName, registryConstructor);
}
@Override
public DistributedObject getDistributedObject(String serviceName, String name) {
checkServiceNameNotNull(serviceName);
checkObjectNameNotNull(name);
ProxyRegistry registry = getOrCreateRegistry(serviceName);
return registry.getOrCreateProxy(name, true);
}
@Override
public void destroyDistributedObject(String serviceName, String name) {
checkServiceNameNotNull(serviceName);
checkObjectNameNotNull(name);
OperationService operationService = nodeEngine.getOperationService();
Collection<Member> members = nodeEngine.getClusterService().getMembers();
Collection<Future> calls = new ArrayList<Future>(members.size());
for (Member member : members) {
if (member.localMember()) {
continue;
}
DistributedObjectDestroyOperation operation = new DistributedObjectDestroyOperation(serviceName, name);
Future f = operationService.createInvocationBuilder(SERVICE_NAME, operation, member.getAddress())
.setTryCount(TRY_COUNT).invoke();
calls.add(f);
}
destroyLocalDistributedObject(serviceName, name, true);
waitWithDeadline(calls, DESTROY_TIMEOUT_SECONDS, TimeUnit.SECONDS, destroyProxyExceptionHandler);
}
public void destroyLocalDistributedObject(String serviceName, String name, boolean fireEvent) {
ProxyRegistry registry = registries.get(serviceName);
if (registry != null) {
registry.destroyProxy(name, fireEvent);
destroyedCounter.inc();
}
RemoteService service = nodeEngine.getService(serviceName);
service.destroyDistributedObject(name);
String message = "DistributedObject[" + service + " -> " + name + "] has been destroyed!";
Throwable cause = new DistributedObjectDestroyedException(message);
nodeEngine.getOperationParker().cancelParkedOperations(serviceName, name, cause);
}
@Override
public Collection<DistributedObject> getDistributedObjects(String serviceName) {
checkServiceNameNotNull(serviceName);
Collection<DistributedObject> result = new LinkedList<DistributedObject>();
ProxyRegistry registry = registries.get(serviceName);
if (registry != null) {
registry.getDistributedObjects(result);
}
return result;
}
@Override
public Collection<String> getDistributedObjectNames(String serviceName) {
checkServiceNameNotNull(serviceName);
ProxyRegistry registry = registries.get(serviceName);
if (registry == null) {
return Collections.emptySet();
} else {
return registry.getDistributedObjectNames();
}
}
@Override
public Collection<DistributedObject> getAllDistributedObjects() {
Collection<DistributedObject> result = new LinkedList<DistributedObject>();
for (ProxyRegistry registry : registries.values()) {
registry.getDistributedObjects(result);
}
return result;
}
@Override
public String addProxyListener(DistributedObjectListener distributedObjectListener) {
String id = UuidUtil.newUnsecureUuidString();
listeners.put(id, distributedObjectListener);
return id;
}
@Override
public boolean removeProxyListener(String registrationId) {
return listeners.remove(registrationId) != null;
}
@Override
public void dispatchEvent(final DistributedObjectEventPacket eventPacket, Object ignore) {
String serviceName = eventPacket.getServiceName();
if (eventPacket.getEventType() == CREATED) {
try {
final ProxyRegistry registry = getOrCreateRegistry(serviceName);
if (!registry.contains(eventPacket.getName())) {
registry.createProxy(eventPacket.getName(), false, true);
// listeners will be called if proxy is created here.
}
} catch (HazelcastInstanceNotActiveException ignored) {
EmptyStatement.ignore(ignored);
}
} else {
final ProxyRegistry registry = registries.get(serviceName);
if (registry != null) {
registry.destroyProxy(eventPacket.getName(), false);
}
}
}
@Override
public Operation getPostJoinOperation() {
Collection<ProxyInfo> proxies = new LinkedList<ProxyInfo>();
for (ProxyRegistry registry : registries.values()) {
registry.getProxyInfos(proxies);
}
return proxies.isEmpty() ? null : new PostJoinProxyOperation(proxies);
}
public void shutdown() {
for (ProxyRegistry registry : registries.values()) {
registry.destroy();
}
registries.clear();
listeners.clear();
}
private static String checkServiceNameNotNull(String serviceName) {
return checkNotNull(serviceName, "Service name is required!");
}
private static String checkObjectNameNotNull(String name) {
return checkNotNull(name, "Object name is required!");
}
}