From ab618d295dd0e49f267b70b4e370abea1103b2cc Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Wed, 2 Sep 2015 18:45:00 -0400 Subject: [PATCH 1/3] binding UDP to localhost so the testsuite will work on environments where UDP is not available due to firewal constraints The server would need to have loopback routes for UDP for this to work. --- .../activemq/artemis/utils/RandomUtil.java | 138 ++++++++++++++++++ .../api/core/UDPBroadcastEndpointFactory.java | 58 ++++++-- .../core/client/ActiveMQClientLogger.java | 9 +- .../artemis/tests/util/RandomUtil.java | 119 +-------------- .../artemis/uri/ConnectionFactoryURITest.java | 21 ++- pom.xml | 2 +- tests/config/logging.properties.trace | 24 +-- ...essageRedistributionWithDiscoveryTest.java | 15 +- .../discovery/DiscoveryBaseTest.java | 6 +- .../discovery/DiscoveryStayAliveTest.java | 47 ++++-- .../integration/discovery/DiscoveryTest.java | 32 +--- .../integration/jms/SimpleJNDIClientTest.java | 6 +- .../ConnectionFactorySerializationTest.java | 4 +- 13 files changed, 270 insertions(+), 211 deletions(-) create mode 100644 artemis-commons/src/main/java/org/apache/activemq/artemis/utils/RandomUtil.java diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/RandomUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/RandomUtil.java new file mode 100644 index 00000000000..0451437ef65 --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/RandomUtil.java @@ -0,0 +1,138 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.activemq.artemis.utils; + +import java.util.Random; + +import org.apache.activemq.artemis.api.core.ActiveMQBuffer; +import org.apache.activemq.artemis.api.core.ActiveMQBuffers; +import org.apache.activemq.artemis.api.core.SimpleString; + +public class RandomUtil { + // Constants ----------------------------------------------------- + + protected static final Random random = new Random(System.currentTimeMillis()); + + // Attributes ---------------------------------------------------- + + // Static -------------------------------------------------------- + + public static String randomString() { + return java.util.UUID.randomUUID().toString(); + } + + public static SimpleString randomSimpleString() { + return new SimpleString(RandomUtil.randomString()); + } + + public static char randomChar() { + return RandomUtil.randomString().charAt(0); + } + + public static long randomLong() { + return RandomUtil.random.nextLong(); + } + + public static long randomPositiveLong() { + return Math.abs(RandomUtil.randomLong()); + } + + public static int randomInt() { + return RandomUtil.random.nextInt(); + } + + public static int randomPositiveInt() { + return Math.abs(RandomUtil.randomInt()); + } + + public static ActiveMQBuffer randomBuffer(final int size, final long... data) { + ActiveMQBuffer buffer = ActiveMQBuffers.fixedBuffer(size + 8 * data.length); + + for (long d : data) { + buffer.writeLong(d); + } + + for (int i = 0; i < size; i++) { + buffer.writeByte(randomByte()); + } + + return buffer; + } + + public static int randomInterval(final int min, final int max) { + return min + randomMax(max - min); + } + + public static int randomMax(final int max) { + int value = randomPositiveInt() % max; + + if (value == 0) { + value = max; + } + + return value; + } + + public static int randomPort() { + return RandomUtil.random.nextInt(65536); + } + + public static short randomShort() { + return (short) RandomUtil.random.nextInt(Short.MAX_VALUE); + } + + public static byte randomByte() { + return Integer.valueOf(RandomUtil.random.nextInt()).byteValue(); + } + + public static boolean randomBoolean() { + return RandomUtil.random.nextBoolean(); + } + + public static byte[] randomBytes() { + return RandomUtil.randomString().getBytes(); + } + + public static byte[] randomBytes(final int length) { + byte[] bytes = new byte[length]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = RandomUtil.randomByte(); + } + return bytes; + } + + public static double randomDouble() { + return RandomUtil.random.nextDouble(); + } + + public static float randomFloat() { + return RandomUtil.random.nextFloat(); + } + + // Constructors -------------------------------------------------- + + // Public -------------------------------------------------------- + + // Package protected --------------------------------------------- + + // Protected ----------------------------------------------------- + + // Private ------------------------------------------------------- + + // Inner classes ------------------------------------------------- +} diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/UDPBroadcastEndpointFactory.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/UDPBroadcastEndpointFactory.java index 193bd315e44..13c69850b3e 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/UDPBroadcastEndpointFactory.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/UDPBroadcastEndpointFactory.java @@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.core.client.ActiveMQClientLogger; +import org.apache.activemq.artemis.utils.RandomUtil; /** * The configuration used to determine how the server will broadcast members. @@ -35,7 +36,10 @@ */ public final class UDPBroadcastEndpointFactory implements BroadcastEndpointFactory { - private transient String localBindAddress = null; + // You can specify a property as a default. This is useful for testsuite running. + // for that reason we won't document this property as the proper way to do it is through configuration. + // these property names can change at any time, so don't use this on production systems + private transient String localBindAddress = getProperty(UDPBroadcastEndpointFactory.class.getName() + ".localBindAddress", null); private transient int localBindPort = -1; @@ -170,9 +174,23 @@ public void openBroadcaster() throws Exception { } else { if (localAddress != null) { - ActiveMQClientLogger.LOGGER.broadcastGroupBindError(); + java.util.Random random = new java.util.Random(System.currentTimeMillis()); + + for (int i = 0; i < 100; i++) { + int nextPort = RandomUtil.randomInterval(3000, 4000); + try { + broadcastingSocket = new DatagramSocket(nextPort, localAddress); + ActiveMQClientLogger.LOGGER.broadcastGroupBindError(localAddress.toString() + ":" + nextPort); + break; + } + catch (Exception e) { + ActiveMQClientLogger.LOGGER.broadcastGroupBindErrorRetry(localAddress.toString() + ":" + nextPort, e); + } + } + } + if (broadcastingSocket == null) { + broadcastingSocket = new DatagramSocket(); } - broadcastingSocket = new DatagramSocket(); } open = true; @@ -231,13 +249,35 @@ private static boolean checkForSolaris() { } private static boolean checkForPresence(String key, String value) { - try { - String tmp = System.getProperty(key); - return tmp != null && tmp.trim().toLowerCase().startsWith(value); - } - catch (Throwable t) { - return false; + String tmp = getProperty(key, null); + return tmp != null && tmp.trim().toLowerCase().startsWith(value); + } + + } + + private static String getProperty(String key, String defaultValue) { + try { + String tmp = System.getProperty(key); + if (tmp == null) { + tmp = defaultValue; } + return tmp; + } + catch (Throwable t) { + ActiveMQClientLogger.LOGGER.warn(t); + return defaultValue; + } + } + + private static int getIntProperty(String key, String defaultValue) { + String value = getProperty(key, defaultValue); + + try { + return Integer.parseInt(value); + } + catch (Throwable t) { + ActiveMQClientLogger.LOGGER.warn(t.getMessage(), t); + return Integer.parseInt(defaultValue); } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientLogger.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientLogger.java index 901797c3d78..f881191796a 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientLogger.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientLogger.java @@ -260,9 +260,9 @@ public interface ActiveMQClientLogger extends BasicLogger { void jvmAllocatedMoreMemory(Long totalMemory1, Long totalMemory2); @LogMessage(level = Logger.Level.WARN) - @Message(id = 212048, value = "local-bind-address specified for broadcast group but no local-bind-port specified so socket will NOT be bound to a local address/port", + @Message(id = 212048, value = "Random address ({0}) was already in use, trying another time", format = Message.Format.MESSAGE_FORMAT) - void broadcastGroupBindError(); + void broadcastGroupBindErrorRetry(String hostAndPort, @Cause Throwable t); @LogMessage(level = Logger.Level.WARN) @Message(id = 212049, @@ -309,6 +309,11 @@ public interface ActiveMQClientLogger extends BasicLogger { @Message(id = 212055, value = "Unable to close consumer", format = Message.Format.MESSAGE_FORMAT) void unableToCloseConsumer(@Cause Exception e); + @LogMessage(level = Logger.Level.WARN) + @Message(id = 212056, value = "local-bind-address specified for broadcast group but no local-bind-port. Using random port for UDP Broadcast ({0})", + format = Message.Format.MESSAGE_FORMAT) + void broadcastGroupBindError(String hostAndPort); + @LogMessage(level = Logger.Level.ERROR) @Message(id = 214000, value = "Failed to call onMessage", format = Message.Format.MESSAGE_FORMAT) void onMessageError(@Cause Throwable e); diff --git a/artemis-core-client/src/test/java/org/apache/activemq/artemis/tests/util/RandomUtil.java b/artemis-core-client/src/test/java/org/apache/activemq/artemis/tests/util/RandomUtil.java index 20a2957e39b..d34cd0cfde6 100644 --- a/artemis-core-client/src/test/java/org/apache/activemq/artemis/tests/util/RandomUtil.java +++ b/artemis-core-client/src/test/java/org/apache/activemq/artemis/tests/util/RandomUtil.java @@ -17,128 +17,11 @@ package org.apache.activemq.artemis.tests.util; import javax.transaction.xa.Xid; -import java.util.Random; -import java.util.UUID; - -import org.apache.activemq.artemis.api.core.ActiveMQBuffer; -import org.apache.activemq.artemis.api.core.ActiveMQBuffers; -import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.transaction.impl.XidImpl; -public final class RandomUtil { - // Constants ----------------------------------------------------- - - private static final Random random = new Random(System.currentTimeMillis()); - - // Attributes ---------------------------------------------------- - - // Static -------------------------------------------------------- - - public static String randomString() { - return UUID.randomUUID().toString(); - } - - public static SimpleString randomSimpleString() { - return new SimpleString(RandomUtil.randomString()); - } - - public static char randomChar() { - return RandomUtil.randomString().charAt(0); - } - - public static long randomLong() { - return RandomUtil.random.nextLong(); - } - - public static long randomPositiveLong() { - return Math.abs(RandomUtil.randomLong()); - } - - public static int randomInt() { - return RandomUtil.random.nextInt(); - } - - public static int randomPositiveInt() { - return Math.abs(RandomUtil.randomInt()); - } - - public static ActiveMQBuffer randomBuffer(final int size, final long... data) { - ActiveMQBuffer buffer = ActiveMQBuffers.fixedBuffer(size + 8 * data.length); - - for (long d : data) { - buffer.writeLong(d); - } - - for (int i = 0; i < size; i++) { - buffer.writeByte(randomByte()); - } - - return buffer; - } - - public static int randomInterval(final int min, final int max) { - return min + randomMax(max - min); - } - - public static int randomMax(final int max) { - int value = randomPositiveInt() % max; - - if (value == 0) { - value = max; - } - - return value; - } - - public static int randomPort() { - return RandomUtil.random.nextInt(65536); - } - - public static short randomShort() { - return (short) RandomUtil.random.nextInt(Short.MAX_VALUE); - } - - public static byte randomByte() { - return Integer.valueOf(RandomUtil.random.nextInt()).byteValue(); - } - - public static boolean randomBoolean() { - return RandomUtil.random.nextBoolean(); - } - - public static byte[] randomBytes() { - return RandomUtil.randomString().getBytes(); - } - - public static byte[] randomBytes(final int length) { - byte[] bytes = new byte[length]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = RandomUtil.randomByte(); - } - return bytes; - } - - public static double randomDouble() { - return RandomUtil.random.nextDouble(); - } - - public static float randomFloat() { - return RandomUtil.random.nextFloat(); - } +public class RandomUtil extends org.apache.activemq.artemis.utils.RandomUtil { public static Xid randomXid() { return new XidImpl(RandomUtil.randomBytes(), RandomUtil.randomInt(), RandomUtil.randomBytes()); } - - // Constructors -------------------------------------------------- - - // Public -------------------------------------------------------- - - // Package protected --------------------------------------------- - - // Protected ----------------------------------------------------- - - // Private ------------------------------------------------------- - - // Inner classes ------------------------------------------------- } diff --git a/artemis-jms-client/src/test/java/org/apache/activemq/artemis/uri/ConnectionFactoryURITest.java b/artemis-jms-client/src/test/java/org/apache/activemq/artemis/uri/ConnectionFactoryURITest.java index ed01ce522cb..d374756522c 100644 --- a/artemis-jms-client/src/test/java/org/apache/activemq/artemis/uri/ConnectionFactoryURITest.java +++ b/artemis-jms-client/src/test/java/org/apache/activemq/artemis/uri/ConnectionFactoryURITest.java @@ -263,21 +263,26 @@ public void testUDPURI() throws Exception { discoveryGroupConfiguration.setName("foo").setRefreshTimeout(12345).setDiscoveryInitialWaitTimeout(5678).setBroadcastEndpointFactory(endpoint); ActiveMQConnectionFactory connectionFactoryWithHA = ActiveMQJMSClient.createConnectionFactoryWithHA(discoveryGroupConfiguration, JMSFactoryType.CF); URI tcp = parser.createSchema("udp", connectionFactoryWithHA); - ActiveMQConnectionFactory factory = parser.newObject(tcp, null); + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(tcp.toString()); DiscoveryGroupConfiguration dgc = factory.getDiscoveryGroupConfiguration(); Assert.assertNotNull(dgc); BroadcastEndpointFactory befc = dgc.getBroadcastEndpointFactory(); Assert.assertNotNull(befc); Assert.assertTrue(befc instanceof UDPBroadcastEndpointFactory); UDPBroadcastEndpointFactory ubgc = (UDPBroadcastEndpointFactory) befc; - Assert.assertEquals(ubgc.getGroupAddress(), "wahey"); - Assert.assertEquals(ubgc.getGroupPort(), 3333); + Assert.assertEquals("wahey", ubgc.getGroupAddress()); + Assert.assertEquals(3333, ubgc.getGroupPort()); + //these 2 are transient - Assert.assertEquals(ubgc.getLocalBindAddress(), null); - Assert.assertEquals(ubgc.getLocalBindPort(), -1); - Assert.assertEquals(dgc.getName(), "foo"); - Assert.assertEquals(dgc.getDiscoveryInitialWaitTimeout(), 5678); - Assert.assertEquals(dgc.getRefreshTimeout(), 12345); + // These will take the System.properties used on the testsuite, + // for that reason we take them as != instead of checking for null + Assert.assertNotEquals("uhuh", ubgc.getLocalBindAddress()); + Assert.assertNotEquals(555, ubgc.getLocalBindPort()); + + Assert.assertEquals("foo", dgc.getName()); + Assert.assertEquals(5678, dgc.getDiscoveryInitialWaitTimeout()); + Assert.assertEquals(12345, dgc.getRefreshTimeout()); BeanUtilsBean bean = new BeanUtilsBean(); checkEquals(bean, connectionFactoryWithHA, factory); diff --git a/pom.xml b/pom.xml index 07c45160a3c..111dffabe14 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dlogging.configuration=file:${activemq.basedir}/tests/config/logging.properties - -Djava.library.path=${activemq.basedir}/artemis-native/bin/ -Djgroups.bind_addr=localhost + -Djava.library.path=${activemq.basedir}/artemis-native/bin/ -Djgroups.bind_addr=localhost -Dorg.apache.activemq.artemis.api.core.UDPBroadcastEndpointFactory.localBindAddress=localhost -Djava.net.preferIPv4Stack=true ${project.basedir} diff --git a/tests/config/logging.properties.trace b/tests/config/logging.properties.trace index 02d52694853..7fe923f89d2 100644 --- a/tests/config/logging.properties.trace +++ b/tests/config/logging.properties.trace @@ -22,37 +22,37 @@ loggers=org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms,org.apache.activemq.artemis.ra,org.apache.activemq.artemis.tests.unit,org.apache.activemq.artemis.tests.integration,org.apache.activemq.artemis.jms.tests # Root logger level -logger.level=INFO +logger.level=TRACE # ActiveMQ Artemis logger levels logger.org.apache.activemq.artemis.core.server.level=TRACE -logger.org.apache.activemq.artemis.journal.level=INFO -logger.org.apache.activemq.artemis.utils.level=INFO -logger.org.apache.activemq.artemis.jms.level=INFO -logger.org.apache.activemq.artemis.ra.level=INFO -logger.org.apache.activemq.artemis.tests.unit.level=INFO -logger.org.apache.activemq.artemis.tests.integration.level=INFO -logger.org.apache.activemq.artemis.jms.tests.level=INFO +logger.org.apache.activemq.artemis.journal.level=TRACE +logger.org.apache.activemq.artemis.utils.level=TRACE +logger.org.apache.activemq.artemis.jms.level=TRACE +logger.org.apache.activemq.artemis.ra.level=TRACE +logger.org.apache.activemq.artemis.tests.unit.level=TRACE +logger.org.apache.activemq.artemis.tests.integration.level=TRACE +logger.org.apache.activemq.artemis.jms.tests.level=TRACE # Root logger handlers -logger.handlers=CONSOLE,TEST +logger.handlers=CONSOLE,TEST,FILE #logger.handlers=CONSOLE,FILE # Console handler configuration handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler handler.CONSOLE.properties=autoFlush -handler.CONSOLE.level=FINE +handler.CONSOLE.level=INFO handler.CONSOLE.autoFlush=true handler.CONSOLE.formatter=PATTERN # File handler configuration handler.FILE=org.jboss.logmanager.handlers.FileHandler -handler.FILE.level=FINE +handler.FILE.level=TRACE handler.FILE.properties=autoFlush,fileName handler.FILE.autoFlush=true handler.FILE.fileName=target/activemq.log handler.FILE.formatter=PATTERN -# Console handler configuration +# Test handler handler.TEST=org.apache.activemq.artemis.logs.AssertionLoggerHandler handler.TEST.level=TRACE handler.TEST.formatter=PATTERN diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/MessageRedistributionWithDiscoveryTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/MessageRedistributionWithDiscoveryTest.java index 4faa93b9eae..32c2c3203a0 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/MessageRedistributionWithDiscoveryTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/distribution/MessageRedistributionWithDiscoveryTest.java @@ -16,23 +16,20 @@ */ package org.apache.activemq.artemis.tests.integration.cluster.distribution; -import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; -import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; -import org.junit.Assert; -import org.junit.Before; - -import org.junit.Test; - -import java.util.ArrayList; - import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; +import java.util.ArrayList; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientProducer; import org.apache.activemq.artemis.api.core.client.ClientSession; +import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; +import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; public class MessageRedistributionWithDiscoveryTest extends ClusterTestBase { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryBaseTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryBaseTest.java index 06bc14ef7b3..2845c9d1153 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryBaseTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryBaseTest.java @@ -56,7 +56,7 @@ public class DiscoveryBaseTest extends ActiveMQTestBase { protected static void verifyBroadcast(BroadcastGroup broadcastGroup, DiscoveryGroup discoveryGroup) throws Exception { broadcastGroup.broadcastConnectors(); - Assert.assertTrue("broadcast received", discoveryGroup.waitForBroadcast(2000)); + Assert.assertTrue("broadcast not received", discoveryGroup.waitForBroadcast(2000)); } /** @@ -147,7 +147,7 @@ protected BroadcastGroupImpl newBroadcast(final String nodeID, int localPort, final InetAddress groupAddress, final int groupPort) throws Exception { - return new BroadcastGroupImpl(new FakeNodeManager(nodeID), name, 0, null, new UDPBroadcastEndpointFactory().setGroupAddress(groupAddress.getHostAddress()).setGroupPort(groupPort).setLocalBindAddress(localAddress != null ? localAddress.getHostAddress() : null).setLocalBindPort(localPort)); + return new BroadcastGroupImpl(new FakeNodeManager(nodeID), name, 0, null, new UDPBroadcastEndpointFactory().setGroupAddress(groupAddress.getHostAddress()).setGroupPort(groupPort).setLocalBindAddress(localAddress != null ? localAddress.getHostAddress() : "localhost").setLocalBindPort(localPort)); } protected DiscoveryGroup newDiscoveryGroup(final String nodeID, @@ -166,7 +166,7 @@ protected DiscoveryGroup newDiscoveryGroup(final String nodeID, final int groupPort, final long timeout, NotificationService notif) throws Exception { - return new DiscoveryGroup(nodeID, name, timeout, new UDPBroadcastEndpointFactory().setGroupAddress(groupAddress.getHostAddress()).setGroupPort(groupPort).setLocalBindAddress(localBindAddress != null ? localBindAddress.getHostAddress() : null), notif); + return new DiscoveryGroup(nodeID, name, timeout, new UDPBroadcastEndpointFactory().setGroupAddress(groupAddress.getHostAddress()).setGroupPort(groupPort).setLocalBindAddress(localBindAddress != null ? localBindAddress.getHostAddress() : "localhost"), notif); } protected final class FakeNodeManager extends NodeManager { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryStayAliveTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryStayAliveTest.java index c3c864594ea..8e51dbe5461 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryStayAliveTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryStayAliveTest.java @@ -30,10 +30,12 @@ import org.apache.activemq.artemis.core.server.cluster.impl.BroadcastGroupImpl; import org.apache.activemq.artemis.tests.util.RandomUtil; import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; +/** + * This is to make sure discovery works fine even when garbled data is sent + */ public class DiscoveryStayAliveTest extends DiscoveryBaseTest { ScheduledExecutorService scheduledExecutorService; @@ -57,7 +59,7 @@ public void testDiscoveryRunning() throws Throwable { final int groupPort = getUDPDiscoveryPort(); final int timeout = 500; - final DiscoveryGroup dg = newDiscoveryGroup(RandomUtil.randomString(), RandomUtil.randomString(), null, groupAddress, groupPort, timeout); + final DiscoveryGroup dg = newDiscoveryGroup(RandomUtil.randomString(), RandomUtil.randomString(), InetAddress.getByName("localhost"), groupAddress, groupPort, timeout); final AtomicInteger errors = new AtomicInteger(0); Thread t = new Thread() { @@ -74,30 +76,45 @@ public void run() { }; t.start(); - BroadcastGroupImpl bg = new BroadcastGroupImpl(new FakeNodeManager("test-nodeID"), RandomUtil.randomString(), 1, scheduledExecutorService, new UDPBroadcastEndpointFactory().setGroupAddress(address1). - setGroupPort(groupPort)); + BroadcastGroupImpl bg = null; + + try { + + bg = new BroadcastGroupImpl(new FakeNodeManager("test-nodeID"), RandomUtil.randomString(), 1, scheduledExecutorService, new UDPBroadcastEndpointFactory().setGroupAddress(address1). + setGroupPort(groupPort)); - bg.start(); + bg.start(); - bg.addConnector(generateTC()); + bg.addConnector(generateTC()); - for (int i = 0; i < 10; i++) { - BroadcastEndpointFactory factoryEndpoint = new UDPBroadcastEndpointFactory().setGroupAddress(address1). - setGroupPort(groupPort); - sendBadData(factoryEndpoint); + for (int i = 0; i < 10; i++) { + BroadcastEndpointFactory factoryEndpoint = new UDPBroadcastEndpointFactory().setGroupAddress(address1). + setGroupPort(groupPort).setLocalBindAddress("localhost"); + sendBadData(factoryEndpoint); + } Thread.sleep(100); assertTrue(t.isAlive()); assertEquals(0, errors.get()); } + finally { - bg.stop(); - dg.stop(); + if (bg != null) { + bg.stop(); + } - t.join(5000); + if (dg != null) { + dg.stop(); + } - Assert.assertFalse(t.isAlive()); + t.join(1000); + // it will retry for a limited time only + for (int i = 0; t.isAlive() && i < 100; i++) { + t.interrupt(); + Thread.sleep(100); + } + } } private static void sendBadData(BroadcastEndpointFactory factoryEndpoint) throws Exception { @@ -120,5 +137,7 @@ private static void sendBadData(BroadcastEndpointFactory factoryEndpoint) throws endpoint.openBroadcaster(); endpoint.broadcast(bytes); + + endpoint.close(true); } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryTest.java index 8dc236e8447..d6f3da44473 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/discovery/DiscoveryTest.java @@ -378,39 +378,11 @@ public void testSimpleBroadcastSpecificNIC() throws Exception { Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); - InetAddress localAddress = null; - - outer: - while (networkInterfaces.hasMoreElements()) { - NetworkInterface networkInterface = networkInterfaces.nextElement(); - if (networkInterface.isLoopback() || networkInterface.isVirtual() || - !networkInterface.isUp() || - !networkInterface.supportsMulticast()) { - continue; - } - - Enumeration en = networkInterface.getInetAddresses(); - - while (en.hasMoreElements()) { - InetAddress ia = en.nextElement(); - - if (ia.getAddress().length == 4) { - localAddress = ia; - - break outer; - } - } - } - - if (localAddress == null) { - log.warn("Can't find address to use"); - - return; - } + InetAddress localAddress = InetAddress.getLoopbackAddress(); log.info("Local address is " + localAddress); - bg = newBroadcast(nodeID, RandomUtil.randomString(), localAddress, 6552, groupAddress, groupPort); + bg = newBroadcast(nodeID, RandomUtil.randomString(), localAddress, -1, groupAddress, groupPort); bg.start(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java index 26ea9ab873f..e0e74a4e0e0 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java @@ -280,7 +280,7 @@ public void testRemoteCFWithUDPWithTransportConfig() throws NamingException, JMS Hashtable props = new Hashtable<>(); props.put(Context.INITIAL_CONTEXT_FACTORY, ActiveMQInitialContextFactory.class.getCanonicalName()); props.put("connectionFactory.myConnectionFactory", "udp://" + getUDPDiscoveryAddress() + ":" + getUDPDiscoveryPort() + "?" + - TransportConstants.LOCAL_ADDRESS_PROP_NAME + "=127.0.0.1&" + + TransportConstants.LOCAL_ADDRESS_PROP_NAME + "=Server1&" + TransportConstants.LOCAL_PORT_PROP_NAME + "=1198&" + ActiveMQInitialContextFactory.REFRESH_TIMEOUT + "=5000&" + ActiveMQInitialContextFactory.DISCOVERY_INITIAL_WAIT_TIMEOUT + "=6000"); @@ -294,8 +294,8 @@ public void testRemoteCFWithUDPWithTransportConfig() throws NamingException, JMS UDPBroadcastEndpointFactory udpBroadcastEndpointFactory = (UDPBroadcastEndpointFactory) discoveryGroupConfiguration.getBroadcastEndpointFactory(); //these 2 are transient so are ignored - Assert.assertEquals(null, udpBroadcastEndpointFactory.getLocalBindAddress()); - Assert.assertEquals(-1, udpBroadcastEndpointFactory.getLocalBindPort()); + Assert.assertNotEquals("Server1", udpBroadcastEndpointFactory.getLocalBindAddress()); + Assert.assertNotEquals(1198, udpBroadcastEndpointFactory.getLocalBindPort()); Assert.assertEquals(getUDPDiscoveryAddress(), udpBroadcastEndpointFactory.getGroupAddress()); Assert.assertEquals(getUDPDiscoveryPort(), udpBroadcastEndpointFactory.getGroupPort()); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/connection/ConnectionFactorySerializationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/connection/ConnectionFactorySerializationTest.java index f86d98d96c9..b800dd02809 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/connection/ConnectionFactorySerializationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/connection/ConnectionFactorySerializationTest.java @@ -82,8 +82,8 @@ public void testConnectionFactoryUDP() throws Exception { Assert.assertEquals(dgc.getRefreshTimeout(), 5000); Assert.assertTrue(dgc.getBroadcastEndpointFactory() instanceof UDPBroadcastEndpointFactory); UDPBroadcastEndpointFactory befc = (UDPBroadcastEndpointFactory) dgc.getBroadcastEndpointFactory(); - Assert.assertEquals(-1, befc.getLocalBindPort()); - Assert.assertEquals(null, befc.getLocalBindAddress()); + Assert.assertEquals(Integer.parseInt(System.getProperty("org.apache.activemq.artemis.api.core.UDPBroadcastEndpointFactory.localBindPort", "-1")), befc.getLocalBindPort()); + Assert.assertEquals(System.getProperty("org.apache.activemq.artemis.api.core.UDPBroadcastEndpointFactory.localBindAddress"), befc.getLocalBindAddress()); Assert.assertEquals(1234, befc.getGroupPort()); Assert.assertEquals("1.2.3.4", befc.getGroupAddress()); } From 220e39ef1fa0173f2da9772c5e4911681bbd056f Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 3 Sep 2015 09:29:15 -0400 Subject: [PATCH 2/3] ARTEMIS-217 fixing dead lock https://issues.apache.org/jira/browse/ARTEMIS-217 fixing dead lock This is using a separate lock for notifications, this way we won't hold a lock while communicating on netty which was the issue here. --- .../artemis/core/client/impl/ServerLocatorImpl.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java index deb17dad828..bed47b7a218 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java @@ -816,7 +816,9 @@ public ClientSessionFactory createSessionFactory() throws ActiveMQException { } } } while (retry); + } + synchronized (topologyArrayGuard) { // We always wait for the topology, as the server // will send a single element if not cluster // so clients can know the id of the server they are connected to @@ -824,7 +826,7 @@ public ClientSessionFactory createSessionFactory() throws ActiveMQException { while (!isClosed() && !receivedTopology && timeout > System.currentTimeMillis()) { // Now wait for the topology try { - wait(1000); + topologyArrayGuard.wait(1000); } catch (InterruptedException e) { throw new ActiveMQInterruptedException(e); @@ -847,7 +849,6 @@ public ClientSessionFactory createSessionFactory() throws ActiveMQException { return factory; } - } public boolean isHA() { @@ -1410,10 +1411,10 @@ public void notifyNodeUp(long uniqueEventID, updateArraysAndPairs(); if (last) { - synchronized (this) { + synchronized (topologyArrayGuard) { receivedTopology = true; // Notify if waiting on getting topology - notifyAll(); + topologyArrayGuard.notifyAll(); } } } From 3c9a49dd96c897dc35e8ab3a8c5df962308a8164 Mon Sep 17 00:00:00 2001 From: Clebert Suconic Date: Thu, 3 Sep 2015 16:41:21 -0400 Subject: [PATCH 3/3] 64 bit compilation --- artemis-native/bin/libartemis-native-64.so | Bin 23984 -> 21442 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/artemis-native/bin/libartemis-native-64.so b/artemis-native/bin/libartemis-native-64.so index aec757ab4c50d1b1e858fdf518984826009729cc..1e24db334ffaca63699bb6f9f3faa7d27df523fa 100755 GIT binary patch literal 21442 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 UQU6R={+=3SARS8Sz^Uc`4-xHV4FCWD