package com.bagri.client.hazelcast.impl; import static com.bagri.core.Constants.*; import static com.bagri.core.server.api.CacheConstants.CN_XDM_CLIENT; import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.xml.xquery.XQItem; import javax.xml.xquery.XQItemType; import javax.xml.xquery.XQSequence; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bagri.client.hazelcast.serialize.XQItemSerializer; import com.bagri.client.hazelcast.serialize.XQItemTypeSerializer; import com.bagri.client.hazelcast.serialize.XQSequenceSerializer; import com.bagri.core.xquery.api.XQProcessor; import com.bagri.support.util.JMXUtils; import com.bagri.xqj.BagriXQDataFactory; import com.hazelcast.client.HazelcastClient; import com.hazelcast.client.config.ClientConfig; import com.hazelcast.client.config.XmlClientConfigBuilder; import com.hazelcast.client.impl.HazelcastClientInstanceImpl; import com.hazelcast.client.impl.HazelcastClientProxy; import com.hazelcast.config.SerializerConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IMap; import com.hazelcast.core.ReplicatedMap; public class ClientManagementImpl { private final static Logger logger = LoggerFactory.getLogger(ClientManagementImpl.class); private final static Map<String, ClientContainer> clients = new HashMap<>(); public HazelcastInstance connect(String clientId, Properties props) { String cKey = getConnectKey(props); synchronized (clients) { ClientContainer cc = clients.get(cKey); if (cc == null) { HazelcastInstance hzClient = initializeHazelcast(props); cc = new ClientContainer(cKey, hzClient); clients.put(cKey, cc); logger.info("connect; new HZ instance created for clientId: {}", clientId); } else { // TODO: check password -> authenticate(); } HazelcastInstance hzClient = cc.hzInstance; if (cc.addClient(clientId)) { //IMap<String, Properties> clientProps = hzClient.getMap(CN_XDM_CLIENT); ReplicatedMap<String, Properties> clientProps = hzClient.getReplicatedMap(CN_XDM_CLIENT); props.remove(pn_client_dataFactory); com.hazelcast.client.impl.HazelcastClientProxy proxy = (com.hazelcast.client.impl.HazelcastClientProxy) hzClient; props.setProperty(pn_client_memberId, proxy.client.getClientClusterService().getLocalClient().getUuid()); props.setProperty(pn_client_connectedAt, new java.util.Date(proxy.getCluster().getClusterTime()).toString()); //clientProps.set(clientId, props); clientProps.put(clientId, props); logger.trace("connect; got new connection for clientId: {}", clientId); } else { logger.info("connect; got existing connection for clientId: {}", clientId); } return hzClient; } } public void connect(String clientId, HazelcastClientProxy hzProxy) { String cKey = getConnectKey(hzProxy); synchronized (clients) { ClientContainer cc = clients.get(cKey); if (cc == null) { cc = new ClientContainer(cKey, hzProxy); clients.put(cKey, cc); logger.info("connect; new container created for clientId: {}", clientId); } else { // check password -> authenticate(); } HazelcastInstance hzClient = cc.hzInstance; if (cc.addClient(clientId)) { //IMap<String, Properties> clientProps = hzClient.getMap(CN_XDM_CLIENT); //props.remove(pn_client_dataFactory); //clientProps.set(clientId, props); logger.trace("connect; got new connection for clientId: {}", clientId); } else { logger.info("connect; got existing connection for clientId: {}", clientId); } } } //private void addClient(ClientContainer cc, String clientId) { // HazelcastInstance hzClient = cc.hzInstance; // if (cc.addClient(clientId)) { // IMap<String, Properties> clientProps = hzClient.getMap(CN_XDM_CLIENT); // props.remove(pn_client_dataFactory); // clientProps.set(clientId, props); // logger.trace("connect; got new connection for clientId: {}", clientId); // } else { // logger.info("connect; got existing connection for clientId: {}", clientId); // } // //} public void disconnect(String clientId) { synchronized (clients) { ClientContainer found = null; for (ClientContainer cc: clients.values()) { logger.trace("disconnect; disconnecting: {}; current clients: {}", clientId, cc.getSize()); try { if (cc.removeClient(clientId)) { found = cc; //IMap<String, Properties> clientProps = cc.hzInstance.getMap(CN_XDM_CLIENT); ReplicatedMap<String, Properties> clientProps = cc.hzInstance.getReplicatedMap(CN_XDM_CLIENT); //clientProps.delete(clientId); clientProps.remove(clientId); logger.trace("disconnect; clientId {} successfuly disconnected", clientId); break; } else { logger.info("disconnect; container don't see client ID: {}; existing: {}", clientId, cc.getClients()); } } catch (Exception ex) { logger.info("disconnect; it seems the server has been stopped already"); } } if (found != null) { if (found.isEmpty()) { if (found.hzInstance.getLifecycleService().isRunning()) { logger.info("disconnect; going to shutdown HZ instance: {}", found.hzInstance); //found.hzInstance.getLifecycleService().shutdown(); // probably, should do something like this: //execService.awaitTermination(100, TimeUnit.SECONDS); found.hzInstance.shutdown(); logger.info("disconnect; the instance {} disconnected", found.hzInstance); } else { logger.info("disconnect; an attempt to shutdown not-running client!"); } clients.remove(found.clientKey); } else { logger.trace("disconnect; disconnected: {}; remaining clients: {}", clientId, found.getSize()); } } else { logger.info("disconnect; can not find container for client: {}", clientId); } } } public String getUserName(String clientId) { ClientContainer cc = getClientContainer(clientId); if (cc == null) { return null; } String[] parts = cc.clientKey.split("::"); return parts[2]; } private String getConnectKey(Properties props) { String schema = props.getProperty(pn_schema_name); String address = props.getProperty(pn_schema_address); String user = props.getProperty(pn_schema_user); String smart = props.getProperty(pn_client_smart); String buffer = props.getProperty(pn_client_bufferSize); return schema + "::" + address + "::" + user + "::" + smart + "::" + buffer; } private String getConnectKey(HazelcastClientProxy hzProxy) { ClientConfig config = hzProxy.getClientConfig(); return config.getGroupConfig().getName() + "::" + config.getNetworkConfig().getAddresses().toString() + "::" + //JMXUtils.getCurrentUser() + "::" + config.getCredentials().getPrincipal() + "::" + config.getNetworkConfig().isSmartRouting() + "::" + config.getNetworkConfig().getSocketOptions().getBufferSize(); } private ClientContainer getClientContainer(String clientId) { for (ClientContainer cc: clients.values()) { if (cc.hasClient(clientId)) { return cc; } } return null; } private HazelcastInstance initializeHazelcast(Properties props) { String schema = props.getProperty(pn_schema_name); String address = props.getProperty(pn_schema_address); String user = props.getProperty(pn_schema_user); String password = props.getProperty(pn_schema_password); String smart = props.getProperty(pn_client_smart); String timeout = props.getProperty(pn_client_loginTimeout); String buffer = props.getProperty(pn_client_bufferSize); String attempts = props.getProperty(pn_client_connectAttempts); String pool = props.getProperty(pn_client_poolSize); String custom = props.getProperty(pn_client_customAuth); //password = encrypt(password); InputStream in = getClass().getResourceAsStream("/hazelcast/hazelcast-client.xml"); ClientConfig config = new XmlClientConfigBuilder(in).build(); config.getGroupConfig().setName(schema); config.getGroupConfig().setPassword(password); String[] members = address.split(","); config.getNetworkConfig().setAddresses(Arrays.asList(members)); if (smart != null) { config.getNetworkConfig().setSmartRouting(smart.equalsIgnoreCase("true")); } if (attempts != null) { int count = Integer.parseInt(attempts); if (count > 0) { config.getNetworkConfig().setConnectionAttemptLimit(count); } } if (timeout != null) { int tm = Integer.parseInt(timeout); // login timeout in seconds if (tm > 0) { config.getNetworkConfig().setConnectionTimeout(tm*1000); } } if (buffer != null) { int size = Integer.parseInt(buffer); if (size > 0) { config.getNetworkConfig().getSocketOptions().setBufferSize(size); } } if (pool != null) { int size = Integer.parseInt(pool); if (size > 0) { config.setExecutorPoolSize(size); } } config.setProperty("hazelcast.logging.type", "slf4j"); if (custom == null || "true".equalsIgnoreCase(custom)) { SecureCredentials creds = new SecureCredentials(user, password); //config.getSecurityConfig().setCredentials(creds); config.setCredentials(creds); } XQProcessor proc = null; BagriXQDataFactory xqFactory = (BagriXQDataFactory) props.get(pn_client_dataFactory); if (xqFactory != null) { proc = xqFactory.getProcessor(); } xqFactory = new BagriXQDataFactory(); xqFactory.setProcessor(proc); XQItemTypeSerializer xqits = new XQItemTypeSerializer(); xqits.setXQDataFactory(xqFactory); config.getSerializationConfig().getSerializerConfigs().add( new SerializerConfig().setTypeClass(XQItemType.class).setImplementation(xqits)); XQItemSerializer xqis = new XQItemSerializer(); xqis.setXQDataFactory(xqFactory); config.getSerializationConfig().getSerializerConfigs().add( new SerializerConfig().setTypeClass(XQItem.class).setImplementation(xqis)); XQSequenceSerializer xqss = new XQSequenceSerializer(); xqss.setXQDataFactory(xqFactory); config.getSerializationConfig().getSerializerConfigs().add( new SerializerConfig().setTypeClass(XQSequence.class).setImplementation(xqss)); logger.debug("initializeHazelcast; config: {}", config); HazelcastInstance hzClient; try { hzClient = HazelcastClient.newHazelcastClient(config); //logger.debug("initializeHazelcast; got HZ: {}", hzInstance); } catch (Throwable ex) { logger.error("initializeHazelcast.error", ex); throw ex; } return hzClient; } private class ClientContainer { private String clientKey; private HazelcastInstance hzInstance; private Set<String> clientIds = new HashSet<>(); ClientContainer(String clientKey, HazelcastInstance hzInstance) { this.clientKey = clientKey; this.hzInstance = hzInstance; } boolean addClient(String clientId) { return clientIds.add(clientId); } Set<String> getClients() { return clientIds; } int getSize() { return clientIds.size(); } boolean hasClient(String clientId) { return clientIds.contains(clientId); } boolean isEmpty() { return clientIds.isEmpty(); } boolean removeClient(String clientId) { return clientIds.remove(clientId); } } }