package com.splout.db.hazelcast; /* * #%L * Splout SQL Server * %% * Copyright (C) 2012 Datasalt Systems S.L. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 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 com.google.common.base.Joiner; import com.hazelcast.config.*; import com.splout.db.common.SploutConfiguration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; /** * Hazelcast configuration can be built programmatically as a facade using the SploutSQL configuration or it can be * loaded from hazelcast.xml in classpath or hazelcast.config env property. To do the latter, use * HazelcastProperties.USE_DEFAULT_OR_XML_CONFIG */ public class HazelcastConfigBuilder { private final static Log log = LogFactory.getLog(HazelcastConfigBuilder.class); @SuppressWarnings("serial") public static class HazelcastConfigBuilderException extends Exception { public HazelcastConfigBuilderException(String message) { super(message); } } public static Config build(SploutConfiguration buConf) throws HazelcastConfigBuilderException { // We can provide a Hazelcast XML if (buConf.getProperty(HazelcastProperties.USE_DEFAULT_OR_XML_CONFIG) != null) { // http://www.hazelcast.com/docs/1.9.4/manual/multi_html/ch11.html // We have to warn that some things will not be available (e.g. persistence) log.warn("Using hazelcast.config env variable or hazelcast.xml in classpath (http://www.hazelcast.com/docs/1.9.4/manual/multi_html/ch11.html). - Please note that persistence for some Coordination structures is not available unless manually provided by your .xml configuration."); return null; // Hazelcast will load the .xml } // Or we can configure it through our facade Config hzConfig = new Config(); String[] iFacesArr = buConf.getStringArray(HazelcastProperties.INTERFACES); if (iFacesArr.length != 0) { log.info("-- Using Hazelcast network interfaces: " + Joiner.on(", ").join(iFacesArr)); InterfacesConfig iFaces = new InterfacesConfig(); for (String strIFace : iFacesArr) { iFaces.addInterface(strIFace.trim()); } iFaces.setEnabled(true); hzConfig.getNetworkConfig().setInterfaces(iFaces); } Integer prop = buConf.getInteger(HazelcastProperties.PORT, -1); if (prop != -1) { log.info("-- Using Hazelcast port: " + prop); hzConfig.getNetworkConfig().setPort(prop); } // Default Backup Count and merge policy MapConfig cfg = new MapConfig(); cfg.setName("default"); cfg.setBackupCount(buConf.getInt(HazelcastProperties.BACKUP_COUNT)); cfg.setMergePolicy("hz.LATEST_UPDATE"); cfg.setInMemoryFormat(InMemoryFormat.OBJECT); // So that comparisons happens by using equals method hzConfig.addMapConfig(cfg); configureJoinMethod(buConf, hzConfig); /* * Miscelaneous */ if (buConf.getBoolean(HazelcastProperties.DISABLE_WAIT_WHEN_JOINING, false)) { log.info("Disabling Hazelcast join wait time."); hzConfig.setProperty("hazelcast.wait.seconds.before.join", "0"); } else { hzConfig.setProperty("hazelcast.wait.seconds.before.join", "20"); } // Loads the rest of custom properties for HZ found in SploutConfiguration loadHazelcastRelatedConfig(buConf, hzConfig); return hzConfig; } protected static void configureJoinMethod(SploutConfiguration buConf, Config hzConfig) throws HazelcastConfigBuilderException { String joinMethod = buConf.getString(HazelcastProperties.JOIN_METHOD); if (joinMethod == null) { throw new HazelcastConfigBuilderException("No join method specified in configuration, must be one of: " + Arrays.toString(HazelcastProperties.JOIN_METHODS.values())); } joinMethod = joinMethod.toUpperCase(); HazelcastProperties.JOIN_METHODS method; try { method = HazelcastProperties.JOIN_METHODS.valueOf(joinMethod); } catch (IllegalArgumentException e) { throw new HazelcastConfigBuilderException("Invalid join method: " + joinMethod + " must be one of: " + Arrays.toString(HazelcastProperties.JOIN_METHODS.values())); } NetworkConfig network = hzConfig.getNetworkConfig(); JoinConfig join = network.getJoin(); // Disable all by default. Then we enable the correct one. join.getMulticastConfig().setEnabled(false); join.getTcpIpConfig().setEnabled(false); join.getAwsConfig().setEnabled(false); if (method.equals(HazelcastProperties.JOIN_METHODS.AWS)) { configureAWS(buConf, join); } else if (method.equals(HazelcastProperties.JOIN_METHODS.TCP)) { configureTCP(buConf, join); } else { configureMulticast(buConf, join); } } protected static void configureAWS(SploutConfiguration buConf, JoinConfig join) throws HazelcastConfigBuilderException { log.info("Configuring Splout for AWS auto-discovery Hazelcast join (http://www.hazelcast.com/docs/1.9.4/manual/multi_html/ch11s02.html)."); join.getAwsConfig().setEnabled(true); String key = buConf.getString(HazelcastProperties.AWS_KEY); if (key == null) { throw new HazelcastConfigBuilderException("Missing AWS Key property (" + HazelcastProperties.AWS_KEY + ")"); } String secretKey = buConf.getString(HazelcastProperties.AWS_SECRET); if (secretKey == null) { throw new HazelcastConfigBuilderException("Missing AWS Secret Key property (" + HazelcastProperties.AWS_SECRET + ")"); } join.getAwsConfig().setAccessKey(key); join.getAwsConfig().setSecretKey(secretKey); // Optionally add the security group String securityGroup = buConf.getString(HazelcastProperties.AWS_SECURITY_GROUP); if (securityGroup != null) { log.info("-- Using security group: " + securityGroup); join.getAwsConfig().setSecurityGroupName(securityGroup); } } protected static void configureMulticast(SploutConfiguration buConf, JoinConfig join) { log.info("Configuring SploutSQL for MULTICAST Hazelcast join."); join.getMulticastConfig().setEnabled(true); String group = buConf.getString(HazelcastProperties.MULTICAST_GROUP); if (group != null) { log.info("-- Using multicast group: " + group); join.getMulticastConfig().setMulticastGroup(group); } Integer port = buConf.getInteger(HazelcastProperties.MULTICAST_PORT, -1); if (port != -1) { log.info("-- Using multicast port: " + port); join.getMulticastConfig().setMulticastPort(port); } } protected static void configureTCP(SploutConfiguration buConf, JoinConfig join) throws HazelcastConfigBuilderException { log.info("Configuring SploutSQL for TCP/IP Hazelcast join."); join.getTcpIpConfig().setEnabled(true); join.getTcpIpConfig().setConnectionTimeoutSeconds( buConf.getInt(HazelcastProperties.TCP_CONNECTION_TIMEOUT_SECONDS, 20)); String[] tcpCluster = buConf.getStringArray(HazelcastProperties.TCP_CLUSTER); if (tcpCluster.length == 0) { throw new HazelcastConfigBuilderException("Enabled TCP join method but missing TCP Cluster key property (" + HazelcastProperties.TCP_CLUSTER + ")"); } try { // Comma separated hosts accepted. ArrayList<String> members = new ArrayList<String>(); members.add(Joiner.on(",").join(tcpCluster)); join.getTcpIpConfig().setMembers(members); } catch (Throwable e) { log.error("Invalid host in TCP cluster", e); throw new HazelcastConfigBuilderException("Invalid host in TCP cluster: " + tcpCluster); } String requiredMember = buConf.getString(HazelcastProperties.TCP_CLUSTER_REQUIRED_MEMBER); if (requiredMember != null) { try { join.getTcpIpConfig().setRequiredMember(requiredMember); } catch (Throwable e) { log.error("Invalid required host in TCP cluster", e); throw new HazelcastConfigBuilderException("Invalid required host in TCP cluster: " + requiredMember); } } } /** * Loads into hzConfig all properties starting by "hazelcast." found * in Splout configuration * * @param buConf * @param hzConfig */ protected static void loadHazelcastRelatedConfig(SploutConfiguration buConf, Config hzConfig) { Iterator<String> hzKeys = buConf.getKeys("hazelcast"); while (hzKeys.hasNext()) { String hzKey = hzKeys.next(); hzConfig.setProperty(hzKey, Joiner.on(",").join(buConf.getStringArray(hzKey))); } } }