Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.glowroot.central.util.ClusterManager Maven / Gradle / Ivy
/*
* Copyright 2017-2018 the original author or authors.
*
* 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 org.glowroot.central.util;
import java.io.File;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import com.google.common.collect.Maps;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.remoting.transport.Address;
import org.infinispan.util.function.SerializableFunction;
import org.infinispan.util.function.TriConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.glowroot.central.util.Cache.CacheLoader;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.TimeUnit.SECONDS;
public abstract class ClusterManager {
private static final Logger logger = LoggerFactory.getLogger(ClusterManager.class);
public static ClusterManager create() {
return new NonClusterManager();
}
public static ClusterManager create(File centralDir, Map jgroupsProperties) {
Map properties = Maps.newHashMap(jgroupsProperties);
String jgroupsConfigurationFile = properties.remove("jgroups.configurationFile");
if (jgroupsConfigurationFile != null) {
String initialNodes = properties.get("jgroups.initialNodes");
if (initialNodes != null) {
// transform from "host1:port1,host2:port2,..." to "host1[port1],host2[port2],..."
properties.put("jgroups.initialNodes",
Pattern.compile(":([0-9]+)").matcher(initialNodes).replaceAll("[$1]"));
}
return new ClusterManagerImpl(centralDir, jgroupsConfigurationFile, properties);
} else {
return new NonClusterManager();
}
}
public abstract Cache createCache(
String cacheName, CacheLoader loader);
public abstract ConcurrentMap createReplicatedMap(
String mapName);
public abstract DistributedExecutionMap createDistributedExecutionMap(
String cacheName);
public abstract void close() throws InterruptedException;
private static class ClusterManagerImpl extends ClusterManager {
private final EmbeddedCacheManager cacheManager;
private ClusterManagerImpl(File centralDir, String jgroupsConfigurationFile,
Map jgroupsProperties) {
GlobalConfiguration configuration = new GlobalConfigurationBuilder()
.transport().defaultTransport()
.addProperty("configurationFile",
getConfigurationFilePropertyValue(centralDir, jgroupsConfigurationFile))
.build();
cacheManager = doWithSystemProperties(jgroupsProperties,
() -> new DefaultCacheManager(configuration));
}
@Override
public Cache createCache(
String cacheName, CacheLoader loader) {
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.clustering()
.cacheMode(CacheMode.INVALIDATION_ASYNC);
cacheManager.defineConfiguration(cacheName, configurationBuilder.build());
return new CacheImpl(cacheManager.getCache(cacheName), loader);
}
@Override
public ConcurrentMap createReplicatedMap(
String mapName) {
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.clustering()
.cacheMode(CacheMode.REPL_ASYNC);
cacheManager.defineConfiguration(mapName, configurationBuilder.build());
return cacheManager.getCache(mapName);
}
@Override
public DistributedExecutionMap createDistributedExecutionMap(
String cacheName) {
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.clustering()
.cacheMode(CacheMode.LOCAL);
cacheManager.defineConfiguration(cacheName, configurationBuilder.build());
return new DistributedExecutionMapImpl(cacheManager.getCache(cacheName));
}
@Override
public void close() throws InterruptedException {
cacheManager.stop();
// org.infinispan.factories.NamedExecutorsFactory.stop() calls shutdownNow() on all
// executors, but does not awaitTermination(), so sleep a bit to allow time
Thread.sleep(1000);
}
private static String getConfigurationFilePropertyValue(File centralDir,
String jgroupsConfigurationFile) {
File file = new File(jgroupsConfigurationFile);
if (file.isAbsolute()) {
return jgroupsConfigurationFile;
}
file = new File(centralDir, jgroupsConfigurationFile);
if (file.exists()) {
return file.getAbsolutePath();
}
if (jgroupsConfigurationFile.equals("jgroups-tcp.xml")
|| jgroupsConfigurationFile.equals("jgroups-udp.xml")) {
return jgroupsConfigurationFile;
}
throw new IllegalStateException(
"Could not find jgroups.configurationFile: " + jgroupsConfigurationFile);
}
private static V doWithSystemProperties(Map properties,
Supplier supplier) {
Map priorSystemProperties = new HashMap<>();
for (Map.Entry entry : properties.entrySet()) {
String key = entry.getKey();
String priorValue = System.getProperty(key);
if (priorValue != null) {
priorSystemProperties.put(key, priorValue);
}
System.setProperty(key, entry.getValue());
}
try {
return supplier.get();
} finally {
for (String key : properties.keySet()) {
String priorValue = priorSystemProperties.get(key);
if (priorValue == null) {
System.clearProperty(key);
} else {
System.setProperty(key, priorValue);
}
}
}
}
}
private static class NonClusterManager extends ClusterManager {
@Override
public Cache createCache(
String cacheName, CacheLoader loader) {
return new NonClusterCacheImpl(new ConcurrentHashMap<>(), loader);
}
@Override
public DistributedExecutionMap createDistributedExecutionMap(
String cacheName) {
return new NonClusterDistributedExecutionMapImpl<>(new ConcurrentHashMap<>());
}
@Override
public ConcurrentMap createReplicatedMap(
String mapName) {
return new ConcurrentHashMap<>();
}
@Override
public void close() {}
}
private static class CacheImpl
implements Cache {
private final org.infinispan.Cache cache;
private final CacheLoader loader;
private CacheImpl(org.infinispan.Cache cache, CacheLoader loader) {
this.cache = cache;
this.loader = loader;
}
@Override
public V get(K key) throws Exception {
V value = cache.get(key);
if (value == null) {
value = loader.load(key);
cache.putForExternalRead(key, value);
}
return value;
}
@Override
public void invalidate(K key) {
cache.remove(key);
}
}
private static class NonClusterCacheImpl
implements Cache {
private final ConcurrentMap cache;
private final CacheLoader loader;
private NonClusterCacheImpl(ConcurrentMap cache, CacheLoader loader) {
this.cache = cache;
this.loader = loader;
}
@Override
public V get(K key) throws Exception {
V value = cache.get(key);
if (value == null) {
value = loader.load(key);
cache.put(key, value);
}
return value;
}
@Override
public void invalidate(K key) {
cache.remove(key);
}
}
private static class DistributedExecutionMapImpl
implements DistributedExecutionMap {
private final org.infinispan.Cache cache;
private DistributedExecutionMapImpl(org.infinispan.Cache cache) {
this.cache = cache;
}
@Override
public @Nullable V get(K key) {
return cache.get(key);
}
@Override
public void put(K key, V value) {
cache.put(key, value);
}
@Override
public void remove(K key, V value) {
cache.remove(key, value);
}
@Override
public Optional execute(String key,
SerializableFunction task) throws Exception {
CollectingConsumer consumer = new CollectingConsumer();
CompletableFuture future = cache.getCacheManager().executor().submitConsumer(
new AdapterFunction(cache.getName(), key, task), consumer);
// TODO short-circuit after receiving one (non-empty and non-shutting-down) response,
// instead of waiting for all responses
future.get(60, SECONDS);
if (consumer.logStackTrace) {
logger.warn("context for remote error(s) logged above",
new Exception("location stack trace"));
}
if (consumer.values.isEmpty()) {
return Optional.empty();
} else {
// TODO first non-shutting-down response
return Optional.of(consumer.values.remove());
}
}
}
private static class NonClusterDistributedExecutionMapImpl
implements DistributedExecutionMap {
private final ConcurrentMap cache;
private NonClusterDistributedExecutionMapImpl(ConcurrentMap cache) {
this.cache = cache;
}
@Override
public @Nullable V get(K key) {
return cache.get(key);
}
@Override
public void put(K key, V value) {
cache.put(key, value);
}
@Override
public void remove(K key, V value) {
cache.remove(key, value);
}
@Override
public Optional execute(String key,
SerializableFunction task) throws Exception {
V value = cache.get(key);
if (value == null) {
return Optional.empty();
}
return Optional.of(task.apply(value));
}
}
@SuppressWarnings("serial")
private static class AdapterFunction
implements SerializableFunction> {
private final String cacheName;
private final String key;
private final SerializableFunction task;
private AdapterFunction(String cacheName, String key, SerializableFunction task) {
this.cacheName = cacheName;
this.key = key;
this.task = task;
}
@Override
public Optional apply(EmbeddedCacheManager cacheManager) {
org.infinispan.Cache cache = cacheManager.getCache(cacheName, false);
if (cache == null) {
return Optional.empty();
}
V value = cache.get(key);
if (value == null) {
return Optional.empty();
}
return Optional.ofNullable(task.apply(value));
}
}
private static class CollectingConsumer
implements TriConsumer, /*@Nullable*/ Throwable> {
private final Queue values = new ConcurrentLinkedQueue<>();
private volatile boolean logStackTrace;
@Override
public void accept(Address address, @Nullable Optional value,
@Nullable Throwable throwable) {
if (throwable != null) {
logger.warn("received error from {}: {}", address, throwable.getMessage(),
throwable);
logStackTrace = true;
return;
}
// value is only null when throwable is not null
if (checkNotNull(value).isPresent()) {
values.add(value.get());
}
}
}
}