package org.radargun.service;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.hazelcast.config.Config;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import com.hazelcast.core.MembershipEvent;
import com.hazelcast.core.MembershipListener;
import org.radargun.Service;
import org.radargun.config.Property;
import org.radargun.logging.Log;
import org.radargun.logging.LogFactory;
import org.radargun.traits.Clustered;
import org.radargun.traits.Lifecycle;
import org.radargun.traits.ProvidesTrait;
import org.radargun.traits.Transactional;
/**
*
* An implementation of CacheWrapper that uses Hazelcast instance as an underlying implementation.
* @author Martin Gencur
*
*/
@Service(doc = "Hazelcast")
public class HazelcastService implements Lifecycle, Clustered {
protected final Log log = LogFactory.getLog(getClass());
private final boolean trace = log.isTraceEnabled();
protected HazelcastInstance hazelcastInstance;
protected List<Membership> membershipHistory = new ArrayList<>();
@Property(name = Service.FILE, doc = "Configuration file.")
private String config;
@Property(name = "cache", doc = "Name of the map ~ cache", deprecatedName = "map")
protected String mapName = "default";
@Property(name = "useTransactions", doc = "Whether the service should use transactions. Default is false.")
protected boolean useTransactions = false;
@ProvidesTrait
public HazelcastService getSelf() {
return this;
}
@ProvidesTrait
public Transactional createTransactional() {
return new HazelcastTransactional(this);
}
@ProvidesTrait
public HazelcastCacheInfo createCacheInfo() {
return new HazelcastCacheInfo(this);
}
@ProvidesTrait
public HazelcastOperations createOperations() {
return new HazelcastOperations(this);
}
@Override
public void start() {
log.info("Creating cache with the following configuration: " + config);
try (InputStream configStream = getAsInputStreamFromClassLoader(config)) {
Config cfg = new XmlConfigBuilder(configStream).build();
hazelcastInstance = Hazelcast.newHazelcastInstance(cfg);
MembershipListener listener = new MembershipListener() {
@Override
public void memberAdded(MembershipEvent membershipEvent) {
updateMembers(membershipEvent.getMembers());
}
@Override
public void memberRemoved(MembershipEvent membershipEvent) {
updateMembers(membershipEvent.getMembers());
}
};
synchronized (this) {
addMembershipListener(listener);
updateMembers(hazelcastInstance.getCluster().getMembers());
}
log.info("Hazelcast configuration:" + hazelcastInstance.getConfig().toString());
} catch (IOException e) {
log.error("Failed to get configuration input stream", e);
}
}
protected void addMembershipListener(MembershipListener listener) {
hazelcastInstance.getCluster().addMembershipListener(listener);
}
@Override
public void stop() {
hazelcastInstance.getLifecycleService().shutdown();
updateMembers(Collections.EMPTY_SET);
hazelcastInstance = null;
}
@Override
public boolean isRunning() {
return hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning();
}
@Override
public boolean isCoordinator() {
return false;
}
@Override
public synchronized Collection<Member> getMembers() {
if (membershipHistory.isEmpty()) return null;
return membershipHistory.get(membershipHistory.size() - 1).members;
}
@Override
public synchronized List<Membership> getMembershipHistory() {
return new ArrayList<>(membershipHistory);
}
protected synchronized void updateMembers(Set<com.hazelcast.core.Member> members) {
ArrayList<Member> mbrs = new ArrayList<>(members.size());
for (com.hazelcast.core.Member m : members) {
mbrs.add(new Member(m.getInetSocketAddress().getHostName() + "(" + m.getUuid() + ")", m.localMember(), false));
}
membershipHistory.add(Membership.create(mbrs));
}
private InputStream getAsInputStreamFromClassLoader(String filename) {
InputStream is = null;
ClassLoader cl = getClass().getClassLoader();
if (cl != null) {
is = cl.getResourceAsStream(filename);
}
if (is == null) {
try {
return new FileInputStream(FileSystems.getDefault().getPath(filename).toFile());
} catch (FileNotFoundException e) {
is = null;
}
}
return is;
}
protected <K, V> IMap<K, V> getMap(String mapName) {
if (mapName == null) {
mapName = this.mapName;
}
return hazelcastInstance.getMap(mapName);
}
}