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:
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<>();
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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" +
|
||||
|
@ -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<>();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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)))
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
51
gossip-protocol-jackson/pom.xml
Normal file
51
gossip-protocol-jackson/pom.xml
Normal 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>
|
@ -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;
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
43
gossip-transport-udp/pom.xml
Normal file
43
gossip-transport-udp/pom.xml
Normal 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>
|
@ -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;
|
@ -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
35
pom.xml
@ -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>
|
||||
|
Reference in New Issue
Block a user