GOSSIP-81 Move Jackson and UDP to their own modules

Part of what makes this work is the test implementation of TransportManager.

This PR is pretty straightforward. A few gotchas though:
* A message signing test was moved into `JacksonTests` because that is
  where the signing actually happens.
* A CRDT serializing test was moved there as well. It's the best place
  for now.
* No UDP tests at all. I plan to fix that in a bit. Reasoning is that it is
  difficult to test any TransportManager implementation without bring up
  a full stack. I plan to address this in the future (GOSSIP-83).
* Simple round trip Jackson serialization tests.
This commit is contained in:
Gary Dusbabek
2017-04-19 13:46:04 -05:00
parent 851cd93e67
commit e3010c8542
24 changed files with 748 additions and 74 deletions

View File

@ -1,4 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@ -35,36 +52,6 @@
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>${metrics.version}</version></dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.vintage.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>${junit.platform.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.teknek</groupId>
<artifactId>tunit</artifactId>
<version>${tunit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>

View File

@ -45,8 +45,8 @@ public class GossipSettings {
private String activeGossipClass = "org.apache.gossip.manager.SimpleActiveGossipper";
private String transportManagerClass = "org.apache.gossip.transport.UdpTransportManager";
private String protocolManagerClass = "org.apache.gossip.protocol.JacksonProtocolManager";
private String transportManagerClass = "org.apache.gossip.transport.udp.UdpTransportManager";
private String protocolManagerClass = "org.apache.gossip.protocol.json.JacksonProtocolManager";
private Map<String,String> activeGossipProperties = new HashMap<>();
@ -230,7 +230,15 @@ public class GossipSettings {
return transportManagerClass;
}
public void setTransportManagerClass(String transportManagerClass) {
this.transportManagerClass = transportManagerClass;
}
public String getProtocolManagerClass() {
return protocolManagerClass;
}
public void setProtocolManagerClass(String protocolManagerClass) {
this.protocolManagerClass = protocolManagerClass;
}
}

View File

@ -185,10 +185,22 @@ public class StartupSettings {
if (cluster == null){
throw new IllegalArgumentException("cluster was null. It is required");
}
String transportClass = jsonObject.has("transport_manager_class") ?
jsonObject.get("transport_manager_class").textValue() :
null;
String protocolClass = jsonObject.has("protocol_manager_class") ?
jsonObject.get("protocol_manager_class").textValue() :
null;
URI uri2 = new URI(uri);
StartupSettings settings = new StartupSettings(id, uri2,
new GossipSettings(gossipInterval, cleanupInterval, windowSize,
minSamples, convictThreshold, distribution), cluster);
GossipSettings gossipSettings = new GossipSettings(gossipInterval, cleanupInterval, windowSize, minSamples,
convictThreshold, distribution);
if (transportClass != null) {
gossipSettings.setTransportManagerClass(transportClass);
}
if (protocolClass != null) {
gossipSettings.setProtocolManagerClass(protocolClass);
}
StartupSettings settings = new StartupSettings(id, uri2, gossipSettings, cluster);
String configMembersDetails = "Config-members [";
JsonNode membersJSON = jsonObject.get("members");
Iterator<JsonNode> it = membersJSON.iterator();

View File

@ -61,7 +61,10 @@ public class PassiveGossipThread implements Runnable {
LOGGER.error("Unable to process message", ex);
}
} catch (IOException e) {
LOGGER.error(e);
// InterruptedException are completely normal here because of the blocking lifecycle.
if (!(e.getCause() instanceof InterruptedException)) {
LOGGER.error(e);
}
keepRunning.set(false);
}
}

View File

@ -66,7 +66,8 @@ public abstract class AbstractTransportManager implements TransportManager {
try {
boolean result = gossipThreadExecutor.awaitTermination(10, TimeUnit.MILLISECONDS);
if (!result) {
LOGGER.error("executor shutdown timed out");
// common when blocking patterns are used to read data from a socket.
LOGGER.warn("executor shutdown timed out");
}
} catch (InterruptedException e) {
LOGGER.error(e);

View File

@ -47,6 +47,8 @@ public class DataTest extends AbstractIntegrationBase {
GossipSettings settings = new GossipSettings();
settings.setPersistRingState(false);
settings.setPersistDataState(false);
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
String cluster = UUID.randomUUID().toString();
int seedNodes = 1;
List<Member> startupMembers = new ArrayList<>();

View File

@ -43,6 +43,8 @@ public class IdAndPropertyTest extends AbstractIntegrationBase {
public void testDatacenterRackGossiper() throws URISyntaxException, UnknownHostException, InterruptedException {
GossipSettings settings = new GossipSettings();
settings.setActiveGossipClass(DatacenterRackAwareActiveGossiper.class.getName());
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
List<Member> startupMembers = new ArrayList<>();
Map<String, String> x = new HashMap<>();
x.put("a", "b");

View File

@ -44,10 +44,14 @@ public class ShutdownDeadtimeTest {
private static final Logger log = Logger.getLogger(ShutdownDeadtimeTest.class);
// Note: this test is floppy depending on the values in GossipSettings (smaller values seem to do harm), and the
// sleep that happens after startup.
@Test
public void DeadNodesDoNotComeAliveAgain()
throws InterruptedException, UnknownHostException, URISyntaxException {
GossipSettings settings = new GossipSettings(100, 10000, 1000, 1, 10.0, "normal");
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
settings.setPersistRingState(false);
settings.setPersistDataState(false);
String cluster = UUID.randomUUID().toString();
@ -70,7 +74,7 @@ public class ShutdownDeadtimeTest {
.build();
clients.add(gossipService);
gossipService.init();
Thread.sleep(1000);
}
TUnit.assertThat(new Callable<Integer>() {
public Integer call() throws Exception {

View File

@ -21,7 +21,6 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
@ -41,28 +40,13 @@ import io.teknek.tunit.TUnit;
public class SignedMessageTest extends AbstractIntegrationBase {
@Test(expected = IllegalArgumentException.class)
public void ifSignMustHaveKeys()
throws URISyntaxException, UnknownHostException, InterruptedException {
String cluster = UUID.randomUUID().toString();
GossipSettings settings = gossiperThatSigns();
List<Member> startupMembers = new ArrayList<>();
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (30000 + 1));
GossipManager gossipService = GossipManagerBuilder.newBuilder()
.cluster(cluster)
.uri(uri)
.id(1 + "")
.gossipMembers(startupMembers)
.gossipSettings(settings)
.build();
gossipService.init();
}
private GossipSettings gossiperThatSigns(){
GossipSettings settings = new GossipSettings();
settings.setPersistRingState(false);
settings.setPersistDataState(false);
settings.setSignMessages(true);
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
return settings;
}

View File

@ -47,11 +47,14 @@ public class StartupSettingsTest {
settingsFile.deleteOnExit();
writeSettingsFile(settingsFile);
URI uri = new URI("udp://" + "127.0.0.1" + ":" + 50000);
GossipSettings firstGossipSettings = new GossipSettings();
firstGossipSettings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
firstGossipSettings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
GossipManager firstService = GossipManagerBuilder.newBuilder()
.cluster(CLUSTER)
.uri(uri)
.id("1")
.gossipSettings(new GossipSettings()).build();
.gossipSettings(firstGossipSettings).build();
firstService.init();
GossipManager manager = GossipManagerBuilder.newBuilder()
.startupSettings(StartupSettings.fromJSONFile(settingsFile)).build();
@ -72,6 +75,8 @@ public class StartupSettingsTest {
" \"cleanup_interval\":10000,\n" +
" \"convict_threshold\":2.6,\n" +
" \"distribution\":\"exponential\",\n" +
" \"transport_manager_class\":\"org.apache.gossip.transport.UnitTestTransportManager\",\n" +
" \"protocol_manager_class\":\"org.apache.gossip.protocol.UnitTestProtocolManager\",\n" +
" \"properties\":{},\n" +
" \"members\":[\n" +
" {\"cluster\": \"" + CLUSTER + "\",\"uri\":\"udp://127.0.0.1:5000\"}\n" +

View File

@ -50,6 +50,8 @@ public class TenNodeThreeSeedTest {
GossipSettings settings = new GossipSettings(1000, 10000, 1000, 1, 1.6, "exponential");
settings.setPersistRingState(false);
settings.setPersistDataState(false);
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
String cluster = UUID.randomUUID().toString();
int seedNodes = 3;
List<Member> startupMembers = new ArrayList<>();

View File

@ -17,15 +17,10 @@
*/
package org.apache.gossip.crdt;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.SortedSet;
import java.util.TreeSet;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.gossip.GossipSettings;
import org.apache.gossip.protocol.JacksonProtocolManager;
import org.junit.Assert;
import org.junit.Test;
@ -85,16 +80,6 @@ public class OrSetTest {
Assert.assertEquals(i.value(), j.value());
}
@Test
public void serialTest() throws InterruptedException, URISyntaxException, IOException {
ObjectMapper objectMapper = JacksonProtocolManager.buildObjectMapper(new GossipSettings());
OrSet<Integer> i = new OrSet<Integer>(new OrSet.Builder<Integer>().add(1).remove(1));
String s = objectMapper.writeValueAsString(i);
@SuppressWarnings("unchecked")
OrSet<Integer> back = objectMapper.readValue(s, OrSet.class);
Assert.assertEquals(back, i);
}
@Test
public void mergeTestSame() {
OrSet<Integer> i = new OrSet<>(19);

View File

@ -40,6 +40,8 @@ public class DataReaperTest {
GossipSettings settings = new GossipSettings();
settings.setPersistRingState(false);
settings.setPersistDataState(false);
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
GossipManager gm = GossipManagerBuilder.newBuilder().cluster("abc").gossipSettings(settings)
.id(myId).uri(URI.create("udp://localhost:6000")).registry(registry).build();
gm.init();
@ -88,6 +90,8 @@ public class DataReaperTest {
String key = "key";
String value = "a";
GossipSettings settings = new GossipSettings();
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
GossipManager gm = GossipManagerBuilder.newBuilder().cluster("abc").gossipSettings(settings)
.id(myId).uri(URI.create("udp://localhost:7000")).registry(registry).build();
gm.init();

View File

@ -35,6 +35,8 @@ public class UserDataPersistenceTest {
private GossipManager sameService() throws URISyntaxException {
GossipSettings settings = new GossipSettings();
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.UnitTestProtocolManager");
return GossipManagerBuilder.newBuilder()
.cluster("a")
.uri(new URI("udp://" + "127.0.0.1" + ":" + (29000 + 1)))

View File

@ -0,0 +1,82 @@
/*
* 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.gossip.protocol;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import org.apache.gossip.GossipSettings;
import org.apache.gossip.manager.PassiveGossipConstants;
import org.apache.gossip.model.Base;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
// doesn't serialize anything besides longs. Uses a static lookup table to read and write objects.
public class UnitTestProtocolManager implements ProtocolManager {
// so it can be shared across gossipers. this works as long as each object has a different memory address.
private static final Map<Long, Base> lookup = new ConcurrentHashMap<>();
private final Meter meter;
public UnitTestProtocolManager(GossipSettings settings, String id, MetricRegistry registry) {
meter = settings.isSignMessages() ?
registry.meter(PassiveGossipConstants.SIGNED_MESSAGE) :
registry.meter(PassiveGossipConstants.UNSIGNED_MESSAGE);
}
private static byte[] longToBytes(long val) {
byte[] b = new byte[8];
b[7] = (byte) (val);
b[6] = (byte) (val >>> 8);
b[5] = (byte) (val >>> 16);
b[4] = (byte) (val >>> 24);
b[3] = (byte) (val >>> 32);
b[2] = (byte) (val >>> 40);
b[1] = (byte) (val >>> 48);
b[0] = (byte) (val >>> 56);
return b;
}
static long bytesToLong(byte[] b) {
return ((b[7] & 0xFFL)) +
((b[6] & 0xFFL) << 8) +
((b[5] & 0xFFL) << 16) +
((b[4] & 0xFFL) << 24) +
((b[3] & 0xFFL) << 32) +
((b[2] & 0xFFL) << 40) +
((b[1] & 0xFFL) << 48) +
(((long) b[0]) << 56);
}
@Override
public byte[] write(Base message) throws IOException {
long hashCode = System.identityHashCode(message);
byte[] serialized = longToBytes(hashCode);
lookup.put(hashCode, message);
meter.mark();
return serialized;
}
@Override
public Base read(byte[] buf) throws IOException {
long hashCode = bytesToLong(buf);
return lookup.remove(hashCode);
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.gossip.transport;
import org.apache.gossip.manager.GossipCore;
import org.apache.gossip.manager.GossipManager;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
/** Only use in unit tests! */
public class UnitTestTransportManager extends AbstractTransportManager {
private static final Map<URI, UnitTestTransportManager> allManagers = new ConcurrentHashMap<>();
private final URI localEndpoint;
private BlockingQueue<byte[]> buffers = new ArrayBlockingQueue<byte[]>(1000);
public UnitTestTransportManager(GossipManager gossipManager, GossipCore gossipCore) {
super(gossipManager, gossipCore);
localEndpoint = gossipManager.getMyself().getUri();
}
@Override
public void send(URI endpoint, byte[] buf) throws IOException {
if (allManagers.containsKey(endpoint)) {
try {
allManagers.get(endpoint).buffers.put(buf);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
@Override
public byte[] read() throws IOException {
try {
return buffers.take();
} catch (InterruptedException ex) {
// probably not the right thing to do, but we'll see.
throw new IOException(ex);
}
}
@Override
public void shutdown() {
allManagers.remove(localEndpoint);
super.shutdown();
}
@Override
public void startEndpoint() {
allManagers.put(localEndpoint, this);
super.startEndpoint();
}
}

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.gossip</groupId>
<artifactId>gossip-parent</artifactId>
<version>0.1.3-incubating-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Gossip Jackson Protocol</name>
<artifactId>gossip-protocol-jackson</artifactId>
<version>0.1.3-incubating-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.gossip</groupId>
<artifactId>gossip-base</artifactId>
<version>0.1.3-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.gossip</groupId>
<artifactId>gossip-base</artifactId>
<version>0.1.3-incubating-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.gossip.protocol;
package org.apache.gossip.protocol.json;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
@ -26,6 +26,7 @@ import org.apache.gossip.crdt.CrdtModule;
import org.apache.gossip.manager.PassiveGossipConstants;
import org.apache.gossip.model.Base;
import org.apache.gossip.model.SignedPayload;
import org.apache.gossip.protocol.ProtocolManager;
import java.io.File;
import java.io.FileInputStream;

View File

@ -0,0 +1,120 @@
/*
* 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.gossip.protocol.json;
import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.gossip.GossipSettings;
import org.apache.gossip.Member;
import org.apache.gossip.crdt.OrSet;
import org.apache.gossip.manager.GossipManager;
import org.apache.gossip.manager.GossipManagerBuilder;
import org.apache.gossip.model.Base;
import org.apache.gossip.protocol.ProtocolManager;
import org.apache.gossip.udp.Trackable;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
public class JacksonTest {
private static GossipSettings simpleSettings(GossipSettings settings) {
settings.setPersistRingState(false);
settings.setPersistDataState(false);
settings.setTransportManagerClass("org.apache.gossip.transport.UnitTestTransportManager");
settings.setProtocolManagerClass("org.apache.gossip.protocol.json.JacksonProtocolManager");
return settings;
}
private static GossipSettings withSigning(GossipSettings settings) {
settings.setSignMessages(true);
return settings;
}
// formerly of SignedMessageTest.
@Test(expected = IllegalArgumentException.class)
public void ifSignMustHaveKeys()
throws URISyntaxException, UnknownHostException, InterruptedException {
String cluster = UUID.randomUUID().toString();
GossipSettings settings = withSigning(simpleSettings(new GossipSettings()));
List<Member> startupMembers = new ArrayList<>();
URI uri = new URI("udp://" + "127.0.0.1" + ":" + (30000 + 1));
GossipManager gossipService = GossipManagerBuilder.newBuilder()
.cluster(cluster)
.uri(uri)
.id(1 + "")
.gossipMembers(startupMembers)
.gossipSettings(settings)
.build();
gossipService.init();
}
@Test
public void jacksonSerialTest() throws InterruptedException, URISyntaxException, IOException {
ObjectMapper objectMapper = JacksonProtocolManager.buildObjectMapper(simpleSettings(new GossipSettings()));
OrSet<Integer> i = new OrSet<Integer>(new OrSet.Builder<Integer>().add(1).remove(1));
String s = objectMapper.writeValueAsString(i);
@SuppressWarnings("unchecked")
OrSet<Integer> back = objectMapper.readValue(s, OrSet.class);
Assert.assertEquals(back, i);
}
@Test
public void testMessageEqualityAssumptions() {
long timeA = System.nanoTime();
long timeB = System.nanoTime();
Assert.assertNotEquals(timeA, timeB);
TestMessage messageA0 = new TestMessage(Long.toHexString(timeA));
TestMessage messageA1 = new TestMessage(Long.toHexString(timeA));
TestMessage messageB = new TestMessage(Long.toHexString(timeB));
Assert.assertEquals(messageA0, messageA1);
Assert.assertFalse(messageA0 == messageA1);
Assert.assertNotEquals(messageA0, messageB);
Assert.assertNotEquals(messageA1, messageB);
}
// ideally, we would test the serializability of every message type, but we just want to make sure this works in
// basic cases.
@Test
public void testMessageSerializationRoundTrip() throws Exception {
ProtocolManager mgr = new JacksonProtocolManager(simpleSettings(new GossipSettings()), "foo", new MetricRegistry());
for (int i = 0; i < 100; i++) {
TestMessage a = new TestMessage(Long.toHexString(System.nanoTime()));
byte[] bytes = mgr.write(a);
TestMessage b = (TestMessage) mgr.read(bytes);
Assert.assertFalse(a == b);
Assert.assertEquals(a, b);
Assert.assertEquals(a.getMapOfThings(), b.getMapOfThings()); // concerned about that one, so explicit check.
}
}
}

View File

@ -0,0 +1,199 @@
/*
* 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.gossip.protocol.json;
import org.apache.gossip.model.Base;
import org.apache.gossip.udp.Trackable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/*
* Here is a test class for serialization. I've tried to include a lot of things in it including nested classes.
* Note that there are no Jackson annotations.
* getters and setters are the keys to making this work without the Jackson annotations.
*/
class TestMessage extends Base implements Trackable {
private String unique;
private String from;
private String uuid;
private String derivedField;
private Subclass otherThing;
private float floatValue;
private double doubleValue;
private Object[] arrayOfThings;
private Map<String, String> mapOfThings = new HashMap<>();
private TestMessage() {
}
TestMessage(String unique) {
this.unique = unique;
from = Integer.toHexString(unique.hashCode());
uuid = Integer.toHexString(from.hashCode());
derivedField = Integer.toHexString(uuid.hashCode());
otherThing = new Subclass(Integer.toHexString(derivedField.hashCode()));
floatValue = (float) unique.hashCode() / (float) from.hashCode();
doubleValue = (double) uuid.hashCode() / (double) derivedField.hashCode();
arrayOfThings = new Object[]{
this.unique, from, uuid, derivedField, otherThing, floatValue, doubleValue
};
String curThing = unique;
for (int i = 0; i < 100; i++) {
String key = Integer.toHexString(curThing.hashCode());
String value = Integer.toHexString(key.hashCode());
curThing = value;
mapOfThings.put(key, value);
}
}
@Override
public String getUriFrom() {
return from;
}
@Override
public void setUriFrom(String uriFrom) {
this.from = uriFrom;
}
@Override
public String getUuid() {
return uuid;
}
@Override
public void setUuid(String uuid) {
this.uuid = uuid;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TestMessage)) return false;
TestMessage that = (TestMessage) o;
return Objects.equals(unique, that.unique) &&
Objects.equals(from, that.from) &&
Objects.equals(getUuid(), that.getUuid()) &&
Objects.equals(derivedField, that.derivedField) &&
Objects.equals(floatValue, that.floatValue) &&
Objects.equals(doubleValue, that.doubleValue) &&
Arrays.equals(arrayOfThings, that.arrayOfThings) &&
Objects.equals(mapOfThings, that.mapOfThings);
}
public String getUnique() {
return unique;
}
public void setUnique(String unique) {
this.unique = unique;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getDerivedField() {
return derivedField;
}
public void setDerivedField(String derivedField) {
this.derivedField = derivedField;
}
public Subclass getOtherThing() {
return otherThing;
}
public void setOtherThing(Subclass otherThing) {
this.otherThing = otherThing;
}
public float getFloatValue() {
return floatValue;
}
public void setFloatValue(float floatValue) {
this.floatValue = floatValue;
}
public double getDoubleValue() {
return doubleValue;
}
public void setDoubleValue(double doubleValue) {
this.doubleValue = doubleValue;
}
public Object[] getArrayOfThings() {
return arrayOfThings;
}
public void setArrayOfThings(Object[] arrayOfThings) {
this.arrayOfThings = arrayOfThings;
}
public Map<String, String> getMapOfThings() {
return mapOfThings;
}
public void setMapOfThings(Map<String, String> mapOfThings) {
this.mapOfThings = mapOfThings;
}
@Override
public int hashCode() {
return Objects.hash(unique, getUriFrom(), getUuid(), derivedField, floatValue, doubleValue, arrayOfThings, mapOfThings);
}
static class Subclass {
private String thing;
public Subclass() {
}
public Subclass(String thing) {
this.thing = thing;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Subclass)) return false;
Subclass subclass = (Subclass) o;
return Objects.equals(thing, subclass.thing);
}
@Override
public int hashCode() {
return Objects.hash(thing);
}
public String getThing() {
return thing;
}
}
}

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.gossip</groupId>
<artifactId>gossip-parent</artifactId>
<version>0.1.3-incubating-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Gossip UDP Transport</name>
<artifactId>gossip-transport-udp</artifactId>
<version>0.1.3-incubating-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.gossip</groupId>
<artifactId>gossip-base</artifactId>
<version>0.1.3-incubating-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@ -15,10 +15,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.gossip.transport;
package org.apache.gossip.transport.udp;
import org.apache.gossip.manager.GossipCore;
import org.apache.gossip.manager.GossipManager;
import org.apache.gossip.transport.AbstractTransportManager;
import org.apache.log4j.Logger;
import java.io.IOException;

View File

@ -0,0 +1,65 @@
/*
* 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.gossip.transport.udp;
import org.apache.gossip.GossipSettings;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class UdpTransportIntegrationTest {
// It's currently impossible to create a UdpTransportManager without bringing up an entire stack.
// This is because AbstractTransportManager creates a PassiveGossipThread (requires GossipManager,
// GossipCore) and also requires those same things plus a MetricsRegistry to create the
// ActiveGossiper.
// TODO: test UDPTransportManger semantics (read and write) in isolation.
// I've written this test to indicate the direction I want things to go.
// Uncomment/Fix it once the coupling issues are worked out.
@Test @Ignore
public void testRoundTrip() {
/*
GossipSettings settings0 = new GossipSettings();
GossipSettings settings1 = new GossipSettings();
UdpTransportManager mgr0 = new UdpTransportManager(settings0);
UdpTransportManager mgr1 = new UdpTransportManager(settings1);
mgr0.startEndpoint();
mgr1.startEndpoint();
mgr0.startActiveGossiper();
mgr1.startActiveGossiper();
// wait a little while for convergence
// perhaps there is a Mockito Whitebox way to foce members
byte[] data = new byte[] {0,1,2,3,4,5};
Future<byte[]> someData = asyncWaitForData(mgr1);
mgr0.send(toURI(settings1), data);
Assert.assertEquals(data, someData.get(1000, TimeUnit.MILLISECONDS));
mgr0.shutdown();
mgr1.shutdown();
*/
}
}

35
pom.xml
View File

@ -56,6 +56,8 @@
<modules>
<module>gossip-base</module>
<module>gossip-transport-udp</module>
<module>gossip-protocol-jackson</module>
</modules>
<description>A peer to peer cluster discovery service</description>
@ -81,6 +83,39 @@
<url>https://issues.apache.org/jira/browse/GOSSIP</url>
</issueManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.vintage.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>${junit.platform.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.teknek</groupId>
<artifactId>tunit</artifactId>
<version>${tunit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>