package cz.cuni.mff.d3s.been.cluster;
import static cz.cuni.mff.d3s.been.cluster.ClusterClientConfiguration.MEMBERS;
import static cz.cuni.mff.d3s.been.cluster.ClusterConfiguration.GROUP;
import static cz.cuni.mff.d3s.been.cluster.ClusterConfiguration.PASSWORD;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hazelcast.client.ClientConfig;
import com.hazelcast.client.HazelcastClient;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import cz.cuni.mff.d3s.been.cluster.context.ClusterContext;
/**
* Singleton for the HazelcastInstance object.
* <p/>
* The class is responsible for joining the cluster with appropriate
* {@link NodeType} and user-defined properties.
* <p/>
*
*
*
* @author Martin Sixta
*/
public final class Instance {
/** Logging */
private static final Logger log = LoggerFactory.getLogger(Instance.class);
/** The singleton Hazelcast instance */
private static HazelcastInstance hazelcastInstance = null;
/** Instance properties */
private static Properties properties = null;
/** Type of the instance */
private static NodeType nodeType = null;
/**
* Path to the default Hazelcast configuration resource.
*/
/**
* Returns type of the node.
*
* @return type of the node
*
* @throws IllegalStateException
* when node is not connected
*/
public static NodeType getNodeType() throws IllegalStateException {
if (!isConnected()) {
throw new IllegalStateException("The node is not connected!");
}
return nodeType;
}
/**
* Returns BEEN's Hazelcast instance
*
* @return BEEN's Hazelcast instance
* @throws IllegalStateException
* when node is not connected
*/
public static HazelcastInstance getInstance() throws IllegalStateException {
if (!isConnected()) {
throw new IllegalStateException("The node is not connected!");
}
return hazelcastInstance;
}
/**
*
* Creates new HazelcastInstance according to supplied type.
*
* The function can be called only once! If you need the hazelcastInstance
* call getInstance().
*
* @param type
* type of the node to be initialized
* @param userProperties
* configuration, can be null
*
* @throws IllegalStateException
* when the node is already connected
* @throws ServiceException
* when the node cannot connect
*/
public static synchronized
void
init(NodeType type, Properties userProperties) throws IllegalStateException, ServiceException {
if (isConnected()) {
throw new IllegalStateException("The node is already connected!");
}
properties = userProperties;
hazelcastInstance = join(type, userProperties);
nodeType = type;
}
/**
* Creates {@link ClusterContext} for this node.
*
* @return Cluster context for this node
*
* @throws IllegalStateException
* when the node is not connected
*/
public static ClusterContext createContext() throws IllegalStateException {
if (!isConnected()) {
throw new IllegalStateException("The node is not connected yet");
}
return new ClusterContext(hazelcastInstance, properties);
}
/**
* Creates native HazelcastInstance instance.
*
* @param host
* host to connect to
* @param port
* port of the host to connect to
* @param groupName
* Hazelcast group name
* @param groupPassword
* Hazelcast group password
* @return HazelcastInstance with specified connection arguments
* @throws RuntimeException
* when the node cannot connect
*/
public static synchronized HazelcastInstance newNativeInstance(String host, int port, String groupName,
String groupPassword) throws RuntimeException {
if (isConnected()) {
throw new RuntimeException("Already connected");
}
final Properties userProperties = new Properties();
userProperties.setProperty(MEMBERS, String.format("%s:%d", host, port));
userProperties.setProperty(GROUP, groupName);
userProperties.setProperty(PASSWORD, groupPassword);
try {
Instance.init(NodeType.NATIVE, userProperties);
return getInstance();
} catch (ServiceException e) {
throw new RuntimeException("Cannot initialize client connection");
}
}
/**
* Shuts down connection to the Hazelcast cluster.
*/
public static synchronized void shutdown() {
if (!isConnected()) {
log.warn("Connection to the cluster already closed");
hazelcastInstance = null;
return;
}
getInstance().getLifecycleService().shutdown();
hazelcastInstance = null;
}
/**
* Creates native HazelcastInstance.
*
* @param userProperties
* properties to be used while connecting
* @return connected native Hazelcast instance
* @throws ServiceException
* when connection cannot be established.
*/
private static HazelcastInstance createNativeInstance(Properties userProperties) throws ServiceException {
ClientConfig clientConfig = InstanceConfigHelper.createClientConfig(userProperties);
return HazelcastClient.newHazelcastClient(clientConfig);
}
/**
* Creates data HazelcastInstance.
*
* @param userProperties
* properties to be used while connecting
* @return connected data Hazelcast instance
* @throws ServiceException
* when connection cannot be established.
*/
private static HazelcastInstance createDataInstance(Properties userProperties) throws ServiceException {
Config config = InstanceConfigHelper.createMemberConfig(userProperties);
return Hazelcast.newHazelcastInstance(config);
}
/**
* Joins this node to the cluster.
*
* @param type
* type of the node
* @param userProperties
* properties to be used
* @return connected node
* @throws ServiceException
* when node cannot join
*/
private static HazelcastInstance join(NodeType type, Properties userProperties) throws ServiceException {
switch (type) {
case DATA:
return createDataInstance(userProperties);
case LITE:
throw new UnsupportedOperationException("LITE node not implemented!");
case NATIVE:
return createNativeInstance(userProperties);
default:
throw new UnsupportedOperationException(nodeType.toString() + " unknown mode!");
}
}
/**
* Whether the node is connected to the cluster
*
* @return true of connected, false otherwise
*/
private static boolean isConnected() {
if (hazelcastInstance != null) {
if (hazelcastInstance instanceof HazelcastClient) {
return ((HazelcastClient) hazelcastInstance).isActive();
}
return true;
}
return false;
}
}