/* * JBoss, Home of Professional Open Source * Copyright 2009 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.infinispan.test.fwk; import org.infinispan.util.LegacyKeySupportSystemProperties; import org.jgroups.conf.ConfiguratorFactory; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.conf.ProtocolStackConfigurator; import org.jgroups.conf.XmlConfigurator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import static org.infinispan.test.fwk.JGroupsConfigBuilder.ProtocolType.*; import static org.infinispan.util.Immutables.immutableMapCopy; /** * This class owns the logic of associating network resources(i.e. ports) with threads, in order to make sure that there * won't be any clashes between multiple clusters running in parallel on same host. Used for parallel test suite. * * @author Mircea.Markus@jboss.com * @author Galder ZamarreƱo */ public class JGroupsConfigBuilder { public static final String JGROUPS_STACK; // Load the XML just once private static final ProtocolStackConfigurator tcpConfigurator = loadTcp(); private static final ProtocolStackConfigurator udpConfigurator = loadUdp(); private static final ThreadLocal<String> threadTcpStartPort = new ThreadLocal<String>() { private final AtomicInteger uniqueAddr = new AtomicInteger(7900); @Override protected String initialValue() { return String.valueOf(uniqueAddr.getAndAdd(50)); } }; /** * Holds unique mcast_addr for each thread used for JGroups channel construction. */ private static final ThreadLocal<String> threadMcastIP = new ThreadLocal<String>() { private final AtomicInteger uniqueAddr = new AtomicInteger(11); @Override protected String initialValue() { return "228.10.10." + uniqueAddr.getAndIncrement(); } }; /** * Holds unique mcast_port for each thread used for JGroups channel construction. */ private static final ThreadLocal<Integer> threadMcastPort = new ThreadLocal<Integer>() { private final AtomicInteger uniquePort = new AtomicInteger(45589); @Override protected Integer initialValue() { return uniquePort.getAndIncrement(); } }; static { JGROUPS_STACK = LegacyKeySupportSystemProperties.getProperty("infinispan.test.jgroups.protocol", "protocol.stack", "tcp"); System.out.println("Transport protocol stack used = " + JGROUPS_STACK); } public static String getJGroupsConfig(String fullTestName, TransportFlags flags) { if (JGROUPS_STACK.equalsIgnoreCase("tcp")) return getTcpConfig(fullTestName, flags); if (JGROUPS_STACK.equalsIgnoreCase("udp")) return getUdpConfig(fullTestName, flags); throw new IllegalStateException("Unknown protocol stack : " + JGROUPS_STACK); } public static String getTcpConfig(String fullTestName, TransportFlags flags) { // With the XML already parsed, make a safe copy of the // protocol stack configurator and use that accordingly. JGroupsProtocolCfg jgroupsCfg = getJGroupsProtocolCfg(tcpConfigurator.getProtocolStack()); if (!flags.withFD()) removeFailureDetectionTcp(jgroupsCfg); if (!flags.withMerge()) removeMerge(jgroupsCfg); if (jgroupsCfg.containsProtocol(TEST_PING)) { replaceTcpStartPort(jgroupsCfg); if (fullTestName == null) return jgroupsCfg.toString(); // IDE run of test else return getTestPingDiscovery(fullTestName, jgroupsCfg); // Cmd line test run } else { return replaceMCastAddressAndPort(jgroupsCfg); } } private static void removeMerge(JGroupsProtocolCfg jgroupsCfg) { jgroupsCfg.removeProtocol(MERGE2); } public static String getUdpConfig(String fullTestName, TransportFlags flags) { JGroupsProtocolCfg jgroupsCfg = getJGroupsProtocolCfg(udpConfigurator.getProtocolStack()); if (!flags.withFD()) removeFailureDetectionUdp(jgroupsCfg); if (!flags.withMerge()) removeMerge(jgroupsCfg); if (jgroupsCfg.containsProtocol(TEST_PING)) { if (fullTestName != null) return getTestPingDiscovery(fullTestName, jgroupsCfg); // Cmd line test run } return replaceMCastAddressAndPort(jgroupsCfg); } /** * Remove all failure detection related * protocols from the given JGroups TCP stack. */ private static void removeFailureDetectionTcp(JGroupsProtocolCfg jgroupsCfg) { jgroupsCfg.removeProtocol(FD) .removeProtocol(FD_SOCK) .removeProtocol(VERIFY_SUSPECT); } private static String getTestPingDiscovery(String fullTestName, JGroupsProtocolCfg jgroupsCfg) { ProtocolType type = TEST_PING; Map<String, String> props = jgroupsCfg.getProtocol(type).getProperties(); props.put("testName", fullTestName); return replaceProperties(jgroupsCfg, props, type); } private static void removeFailureDetectionUdp(JGroupsProtocolCfg jgroupsCfg) { jgroupsCfg.removeProtocol(FD_SOCK).removeProtocol(FD_ALL); } private static String replaceMCastAddressAndPort(JGroupsProtocolCfg jgroupsCfg) { ProtocolType type = UDP; Map<String, String> props = jgroupsCfg.getProtocol(type).getProperties(); props.put("mcast_addr", threadMcastIP.get()); props.put("mcast_port", threadMcastPort.get().toString()); return replaceProperties(jgroupsCfg, props, type); } private static String replaceTcpStartPort(JGroupsProtocolCfg jgroupsCfg) { ProtocolType type = TCP; Map<String, String> props = jgroupsCfg.getProtocol(type).getProperties(); props.put("bind_port", threadTcpStartPort.get()); return replaceProperties(jgroupsCfg, props, type); } private static String replaceProperties( JGroupsProtocolCfg cfg, Map<String, String> newProps, ProtocolType type) { ProtocolConfiguration protocol = cfg.getProtocol(type); ProtocolConfiguration newProtocol = new ProtocolConfiguration(protocol.getProtocolName(), newProps); cfg.replaceProtocol(type, newProtocol); return cfg.toString(); } private static ProtocolStackConfigurator loadTcp() { try { return ConfiguratorFactory.getStackConfigurator("stacks/tcp.xml"); } catch (Exception e) { throw new RuntimeException(e); } } private static ProtocolStackConfigurator loadUdp() { try { return ConfiguratorFactory.getStackConfigurator("stacks/udp.xml"); } catch (Exception e) { throw new RuntimeException(e); } } private static JGroupsProtocolCfg getJGroupsProtocolCfg(List<ProtocolConfiguration> baseStack) { JGroupsXmxlConfigurator configurator = new JGroupsXmxlConfigurator(baseStack); List<ProtocolConfiguration> protoStack = configurator.getProtocolStack(); Map<ProtocolType, ProtocolConfiguration> protoMap = new HashMap<ProtocolType, ProtocolConfiguration>(protoStack.size()); for (ProtocolConfiguration cfg : protoStack) protoMap.put(getProtocolType(cfg.getProtocolName()), cfg); return new JGroupsProtocolCfg(protoMap, configurator); } private static ProtocolType getProtocolType(String name) { int dotIndex = name.lastIndexOf("."); return ProtocolType.valueOf( dotIndex == -1 ? name : name.substring(dotIndex + 1, name.length())); } static class JGroupsXmxlConfigurator extends XmlConfigurator { protected JGroupsXmxlConfigurator(List<ProtocolConfiguration> protocols) { super(copy(protocols)); } static List<ProtocolConfiguration> copy(List<ProtocolConfiguration> protocols) { // Make a safe copy of the protocol stack to avoid concurrent modification issues List<ProtocolConfiguration> copy = new ArrayList<ProtocolConfiguration>(protocols.size()); for (ProtocolConfiguration p : protocols) copy.add(new ProtocolConfiguration( p.getProtocolName(), immutableMapCopy(p.getProperties()))); return copy; } } static class JGroupsProtocolCfg { final Map<ProtocolType, ProtocolConfiguration> protoMap; final ProtocolStackConfigurator configurator; JGroupsProtocolCfg(Map<ProtocolType, ProtocolConfiguration> protoMap, ProtocolStackConfigurator configurator) { this.protoMap = protoMap; this.configurator = configurator; } JGroupsProtocolCfg addProtocol(ProtocolType type, ProtocolConfiguration cfg, int position) { protoMap.put(type, cfg); configurator.getProtocolStack().add(position, cfg); return this; } JGroupsProtocolCfg removeProtocol(ProtocolType type) { // Update the stack and map configurator.getProtocolStack().remove(protoMap.remove(type)); return this; } ProtocolConfiguration getProtocol(ProtocolType type) { return protoMap.get(type); } boolean containsProtocol(ProtocolType type) { return getProtocol(type) != null; } JGroupsProtocolCfg replaceProtocol(ProtocolType type, ProtocolConfiguration newCfg) { ProtocolConfiguration oldCfg = protoMap.get(type); int position = configurator.getProtocolStack().indexOf(oldCfg); // Remove protocol and put new configuration in same position return removeProtocol(type).addProtocol(type, newCfg, position); } @Override public String toString() { return configurator.getProtocolStackString(); } } enum ProtocolType { TCP, UDP, MPING, PING, TCPPING, TEST_PING, MERGE2, FD_SOCK, FD, VERIFY_SUSPECT, FD_ALL, BARRIER, NAKACK, UNICAST, NAKACK2, UNICAST2, RSVP, STABLE, GMS, UFC, MFC, FC, FRAG2, STREAMING_STATE_TRANSFER, SEQUENCER, TOA; } }