package com.tesora.dve.hazelcast; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.net.InetSocketAddress; import java.sql.Connection; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.apache.log4j.Logger; import com.hazelcast.client.ClientConfig; import com.hazelcast.client.HazelcastClient; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.LifecycleEvent; import com.hazelcast.core.LifecycleListener; import com.hazelcast.core.MembershipEvent; import com.hazelcast.core.MembershipListener; public class HazelcastGroupClient extends HazelcastGroupMember implements LifecycleListener, MembershipListener { static Logger logger = Logger.getLogger(HazelcastGroupClient.class); private HazelcastClient ourHazelcastInstance; private Properties props; private boolean autoReconnect = true; private boolean waitForServers = true; public HazelcastGroupClient() { } public void startHazelcastClient(Properties props) throws Exception { this.props = props; ClientConfig clientConfig = new ClientConfig(); clientConfig.getGroupConfig().setName(HAZELCAST_GROUP_NAME).setPassword(HAZELCAST_GROUP_PASSWORD); List<String> servers = waitForServers(props); clientConfig.setAddresses(servers); clientConfig.setInitialConnectionAttemptLimit(3); clientConfig.setReconnectionAttemptLimit(3); ourHazelcastInstance = HazelcastClient.newHazelcastClient(clientConfig); ourHazelcastInstance.getLifecycleService().addLifecycleListener(this); ourHazelcastInstance.getCluster().addMembershipListener(this); logClusterMembers(); } public void stopHazelcastClient() { autoReconnect = false; waitForServers = false; cleanup(); } public boolean isConnected() { try { return getOurHazelcastInstance().getLifecycleService().isRunning(); } catch (Throwable t) { logger.debug("Group client not connected", t); return false; } } @Override protected HazelcastInstance getOurHazelcastInstance() { if (ourHazelcastInstance == null) { throw new IllegalStateException("Client is not connected."); } return ourHazelcastInstance; } @Override public void memberAdded(MembershipEvent event) { logClusterMembers(); } @Override public void memberRemoved(MembershipEvent event) { logClusterMembers(); } @Override public void stateChanged(LifecycleEvent event) { switch (event.getState()) { case CLIENT_CONNECTION_OPENED: logger.info("Connected to DVE cluster."); break; case CLIENT_CONNECTION_LOST: logger.info("Disconnected from DVE cluster."); break; case SHUTDOWN: cleanup(); if (autoReconnect) { reconnect(); } break; default: // logger.debug(event); } } private void reconnect() { Thread t = new Thread() { public void run() { logger.info("Attempting to reconnect..."); while (autoReconnect && !isConnected()) { try { startHazelcastClient(props); } catch (Exception e) { logger.debug("Unable to reconnect to cluster", e); } } } }; t.setName("parlb-client-reconnect"); t.start(); } private void logClusterMembers() { List<InetSocketAddress> members = getMembers(); logger.info("Available servers: " + members.size()); if (!members.isEmpty()) { for (InetSocketAddress address : members) { logger.info("... " + address); } } } private List<String> waitForServers(Properties props) throws Exception { Connection con = null; List<String> servers = new ArrayList<String>(); try { con = getDBConnection(props); servers = findAllRegisteredServers(con); if (servers.isEmpty() && waitForServers) { logger.info("No registered servers. Waiting..."); for (int attempt = 0; waitForServers && servers.isEmpty(); attempt++) { // try for a minute, then back off Thread.sleep(attempt < 12 ? 5000 : 30000); servers = findAllRegisteredServers(con); } logger.info("Found " + servers.size() + " registered server(s)."); logger.debug("Registered servers: " + servers); } } finally { if (con != null) con.close(); } return servers; } private void cleanup() { if (ourHazelcastInstance != null) { ourHazelcastInstance.getCluster().removeMembershipListener(this); ourHazelcastInstance.getLifecycleService().removeLifecycleListener(this); ourHazelcastInstance.getLifecycleService().shutdown(); } ourHazelcastInstance = null; } }