package org.radargun.service;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.configuration.Configuration;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
import org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller;
import org.infinispan.protostream.MessageMarshaller;
import org.infinispan.protostream.SerializationContext;
import org.radargun.Service;
import org.radargun.config.Converter;
import org.radargun.config.DefinitionElement;
import org.radargun.config.Init;
import org.radargun.config.Property;
import org.radargun.logging.Log;
import org.radargun.logging.LogFactory;
import org.radargun.traits.ProvidesTrait;
import org.radargun.traits.Queryable;
@Service(doc = Infinispan60HotrodService.SERVICE_DESCRIPTION)
public class Infinispan60HotrodService extends InfinispanHotrodService {
protected static final Pattern ADDRESS_PATTERN = Pattern
.compile("(\\[([0-9A-Fa-f:]+)\\]|([^:/?#]*))(?::(\\d*))?");
protected static final String CLASS_PATTERN = "([\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{L}_$][\\p{L}\\p{N}_$]*";
protected static final Log log = LogFactory.getLog(Infinispan60HotrodService.class);
@Property(doc = "Enables query functionality. This requires using the ProtostreamMarshaller," +
"defining the objects and involves further overhead. Default is false.")
private boolean enableQuery = false;
// TODO: use complexConverter when this will be implemented for setup.properties
@Property(doc = "Classes that should be registered as marshalled. By default, none.",
converter = RegisteredClassConverter.class)
protected List<RegisteredClass> classes;
@Property(doc = "Paths to the .protobin files. Defaul is query/values.protobin")
protected String[] protofiles = new String[] {"/query/values.protobin"};
@Property(doc = "Name of the cluster (used for JMX operations). Default is 'default'.")
protected String clusterName = "default";
@Property(doc = "JMX port to connect the server. Default is 9999.")
protected int jmxPort = 9999;
@Property(doc = "JMX Domain name for components looked up. Default is 'jboss.infinispan'")
protected String jmxDomain = "jboss.infinispan";
@Property(doc = "Maximal amount of active connections to single server. Default is unlimited.")
protected int maxConnectionsServer = -1;
@Property(doc = "Maximal amount of active connections to all servers. Default is unlimited.")
protected int maxConnectionsTotal = -1;
protected ArrayList<String> serverHostnames = new ArrayList<String>();
protected InfinispanHotrodQueryable queryable;
protected Configuration configuration;
@Init
public void init() {
configuration = getDefaultHotRodConfig().build();
}
protected ConfigurationBuilder getDefaultHotRodConfig() {
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.connectionPool().maxActive(maxConnectionsServer).maxTotal(maxConnectionsTotal);
for (String server : servers.split(";")) {
Matcher matcher = ADDRESS_PATTERN.matcher(server);
if (!matcher.matches()) {
log.error("Could not parse server address from " + server);
continue;
}
String v6host = matcher.group(2);
String v4host = matcher.group(3);
String host = v6host != null ? v6host : v4host;
String portString = matcher.group(4);
int port = portString == null
? ConfigurationProperties.DEFAULT_HOTROD_PORT
: Integer.parseInt(portString);
serverHostnames.add(host);
builder.addServer().host(host).port(port);
}
if (enableQuery) {
queryable = createQueryable();
ProtoStreamMarshaller marshaller = new ProtoStreamMarshaller();
builder.marshaller(marshaller);
SerializationContext context = marshaller.getSerializationContext();
queryable.registerProtofilesLocal(context);
// remote registration has to be delayed until we have running servers
// register marshallers
registerMarshallers(context);
}
return builder;
}
protected InfinispanHotrodQueryable createQueryable() {
return new InfinispanHotrodQueryable(this);
}
protected void registerMarshallers(SerializationContext context) {
for (RegisteredClass rc : classes) {
try {
context.registerMarshaller(rc.clazz, rc.getMarshaller());
} catch (Exception e) {
throw new IllegalArgumentException("Could not instantiate marshaller for " + rc.clazz, e);
}
}
}
@ProvidesTrait
public Queryable getQueryable() {
return new InfinispanHotrodQueryable(this);
}
@ProvidesTrait
public Infinispan60HotRodCacheInfo creeateCacheInfo() {
return new Infinispan60HotRodCacheInfo(this);
}
@Override
public void start() {
managerNoReturn = new RemoteCacheManager(configuration, true);
managerForceReturn = new RemoteCacheManager(configuration, true);
if (queryable != null) {
queryable.registerProtofilesRemote();
}
}
// TODO: this is prepared for ReflexiveListConverter
@DefinitionElement(name = "class", doc = "Class that should be registered for protostream.")
protected static class RegisteredClass<T> {
@Property(name = "", doc = "Full name of the class that should be registered.", optional = false, converter = ClassLoadConverter.class)
public Class<T> clazz;
@Property(doc = "Full name of the class marshaller that should be used. By default, inner class with name 'Marshaller' is used.",
converter = ClassLoadConverter.class)
Class<? extends MessageMarshaller<T>> marshaller;
public RegisteredClass() {
}
public RegisteredClass(Class<T> clazz, Class<? extends MessageMarshaller<T>> marshaller) {
this.clazz = clazz;
this.marshaller = marshaller;
}
public MessageMarshaller<T> getMarshaller() throws IllegalAccessException, InstantiationException {
if (marshaller != null) {
if (MessageMarshaller.class.isAssignableFrom(marshaller)) {
throw new IllegalArgumentException(marshaller.getName() + " does not inherit from MessageMarshaller");
}
return marshaller.newInstance();
}
for (Class inner : clazz.getClasses()) {
if (!"Marshaller".equals(inner.getSimpleName())) {
log.trace(inner.getName() + " is not called Marshaller");
} else if (!MessageMarshaller.class.isAssignableFrom(inner)) {
log.trace(inner.getName() + " does not inherit from MessageMarshaller");
} else if (!Modifier.isStatic(inner.getModifiers())) {
log.trace(inner.getName() + " is not static class");
} else {
return (MessageMarshaller<T>) inner.newInstance();
}
}
throw new IllegalStateException("No marshaller class");
}
}
// TODO: this is not used currently
private static class ClassLoadConverter implements Converter<Class<?>> {
@Override
public Class<?> convert(String string, Type type) {
try {
return Class.forName(string);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot instantiate class " + string, e);
}
}
@Override
public String convertToString(Class<?> value) {
return value == null ? null : value.getName();
}
@Override
public String allowedPattern(Type type) {
return CLASS_PATTERN;
}
}
private static class RegisteredClassConverter implements Converter<List<RegisteredClass>> {
@Override
public List<RegisteredClass> convert(String string, Type type) {
String[] parts = string.split(";", 0);
ArrayList<RegisteredClass> list = new ArrayList<RegisteredClass>(parts.length);
for (String part : parts) {
if (part.trim().isEmpty()) continue;
int colon = part.indexOf(':');
String className, marshaller = null;
if (colon < 0) {
className = part.trim();
} else {
className = part.substring(0, colon).trim();
marshaller = part.substring(colon + 1).trim();
}
try {
list.add(new RegisteredClass(Class.forName(className),
marshaller == null ? null : Class.forName(marshaller)));
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot find class " + part, e);
}
}
return list;
}
@Override
public String convertToString(List<RegisteredClass> values) {
StringBuilder sb = new StringBuilder();
boolean first = true;
if (values != null) {
for (RegisteredClass rc : values) {
if (!first) sb.append(", ");
first = false;
sb.append(rc.clazz.getName());
if (rc.marshaller != null) {
sb.append(':').append(rc.marshaller.getName());
}
}
}
return sb.toString();
}
@Override
public String allowedPattern(Type type) {
return CLASS_PATTERN + "(:" + CLASS_PATTERN + ")?(;\\s*" + CLASS_PATTERN + "(:" + CLASS_PATTERN + ")?)*";
}
}
}