Repository: curator Updated Branches: refs/heads/CURATOR-394 [created] 070f1c9e4
CURATOR-275 introduced a new field into ServiceInstance. This caused a potential UnrecognizedPropertyException in older clients that read newly serialized ServiceInstances. Added an alternate ctor to JsonInstanceSerializer with a compatibleSerializationMode option. when set to true, the new enabled field of ServiceInstance is not serialized. Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/070f1c9e Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/070f1c9e Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/070f1c9e Branch: refs/heads/CURATOR-394 Commit: 070f1c9e4dae947ba21b015ea5027c3d20d170bd Parents: 2672049 Author: randgalt <randg...@apache.org> Authored: Fri Mar 24 19:22:48 2017 -0500 Committer: randgalt <randg...@apache.org> Committed: Fri Mar 24 19:22:48 2017 -0500 ---------------------------------------------------------------------- .../x/discovery/ServiceInstanceBuilder.java | 2 + .../details/JsonInstanceSerializer.java | 44 ++++- .../x/discovery/details/OldServiceInstance.java | 196 +++++++++++++++++++ .../x/discovery/TestJsonInstanceSerializer.java | 136 +++++++------ .../x/discovery/details/NewServiceInstance.java | 148 ++++++++++++++ ...TestJsonInstanceSerializerCompatibility.java | 79 ++++++++ 6 files changed, 543 insertions(+), 62 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/070f1c9e/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceInstanceBuilder.java ---------------------------------------------------------------------- diff --git a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceInstanceBuilder.java b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceInstanceBuilder.java index c2ea40e..0f6a0b7 100644 --- a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceInstanceBuilder.java +++ b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceInstanceBuilder.java @@ -19,6 +19,7 @@ package org.apache.curator.x.discovery; import com.google.common.collect.Lists; +import org.codehaus.jackson.annotate.JsonIgnoreProperties; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; @@ -30,6 +31,7 @@ import java.util.concurrent.atomic.AtomicReference; /** * Builder for service instances */ +@JsonIgnoreProperties(ignoreUnknown = true) public class ServiceInstanceBuilder<T> { private T payload; http://git-wip-us.apache.org/repos/asf/curator/blob/070f1c9e/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/JsonInstanceSerializer.java ---------------------------------------------------------------------- diff --git a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/JsonInstanceSerializer.java b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/JsonInstanceSerializer.java index b7ddbc2..983c48d 100644 --- a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/JsonInstanceSerializer.java +++ b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/JsonInstanceSerializer.java @@ -16,12 +16,14 @@ * specific language governing permissions and limitations * under the License. */ + package org.apache.curator.x.discovery.details; +import com.google.common.annotations.VisibleForTesting; import org.apache.curator.x.discovery.ServiceInstance; +import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.JavaType; -import java.io.ByteArrayOutputStream; /** * A serializer that uses Jackson to serialize/deserialize as JSON. IMPORTANT: The instance @@ -29,17 +31,42 @@ import java.io.ByteArrayOutputStream; */ public class JsonInstanceSerializer<T> implements InstanceSerializer<T> { - private final ObjectMapper mapper; - private final Class<T> payloadClass; - private final JavaType type; + private final ObjectMapper mapper; + private final Class<T> payloadClass; + private final boolean compatibleSerializationMode; + private final JavaType type; /** * @param payloadClass used to validate payloads when deserializing */ public JsonInstanceSerializer(Class<T> payloadClass) { + this(payloadClass, false, false); + } + + /** + * CURATOR-275 introduced a new field into {@link org.apache.curator.x.discovery.ServiceInstance}. This caused a potential + * {@link org.codehaus.jackson.map.exc.UnrecognizedPropertyException} in older clients that + * read newly serialized ServiceInstances. If you are susceptible to this you should set the + * serializer to be an instance of {@link org.apache.curator.x.discovery.details.JsonInstanceSerializer} + * with <code>compatibleSerializationMode</code> set to true. IMPORTANT: when this is done, the new <code>enabled</code> + * field of ServiceInstance is <strong>not</strong> serialized. + * + * @param payloadClass used to validate payloads when deserializing + * @param compatibleSerializationMode pass true to serialize in a manner that supports clients pre-CURATOR-275 + */ + public JsonInstanceSerializer(Class<T> payloadClass, boolean compatibleSerializationMode) + { + this(payloadClass, compatibleSerializationMode, false); + } + + @VisibleForTesting + JsonInstanceSerializer(Class<T> payloadClass, boolean compatibleSerializationMode, boolean failOnUnknownProperties) + { this.payloadClass = payloadClass; + this.compatibleSerializationMode = compatibleSerializationMode; mapper = new ObjectMapper(); + mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties); type = mapper.getTypeFactory().constructType(ServiceInstance.class); } @@ -55,8 +82,11 @@ public class JsonInstanceSerializer<T> implements InstanceSerializer<T> @Override public byte[] serialize(ServiceInstance<T> instance) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - mapper.writeValue(out, instance); - return out.toByteArray(); + if ( compatibleSerializationMode ) + { + OldServiceInstance<T> compatible = new OldServiceInstance<T>(instance.getName(), instance.getId(), instance.getAddress(), instance.getPort(), instance.getSslPort(), instance.getPayload(), instance.getRegistrationTimeUTC(), instance.getServiceType(), instance.getUriSpec()); + return mapper.writeValueAsBytes(compatible); + } + return mapper.writeValueAsBytes(instance); } } http://git-wip-us.apache.org/repos/asf/curator/blob/070f1c9e/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/OldServiceInstance.java ---------------------------------------------------------------------- diff --git a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/OldServiceInstance.java b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/OldServiceInstance.java new file mode 100644 index 0000000..253b274 --- /dev/null +++ b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/OldServiceInstance.java @@ -0,0 +1,196 @@ +/** + * 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.curator.x.discovery.details; + +import com.google.common.base.Preconditions; +import org.apache.curator.x.discovery.ServiceType; +import org.apache.curator.x.discovery.UriSpec; +import org.codehaus.jackson.annotate.JsonTypeInfo; +import org.codehaus.jackson.annotate.JsonTypeInfo.Id; + +/** + * POJO that represents a service instance + */ +class OldServiceInstance<T> +{ + private final String name; + private final String id; + private final String address; + private final Integer port; + private final Integer sslPort; + private final T payload; + private final long registrationTimeUTC; + private final ServiceType serviceType; + private final UriSpec uriSpec; + + /** + * @param name name of the service + * @param id id of this instance (must be unique) + * @param address address of this instance + * @param port the port for this instance or null + * @param sslPort the SSL port for this instance or null + * @param payload the payload for this instance or null + * @param registrationTimeUTC the time (in UTC) of the registration + * @param serviceType type of the service + * @param uriSpec the uri spec or null + */ + OldServiceInstance(String name, String id, String address, Integer port, Integer sslPort, T payload, long registrationTimeUTC, ServiceType serviceType, UriSpec uriSpec) + { + name = Preconditions.checkNotNull(name, "name cannot be null"); + id = Preconditions.checkNotNull(id, "id cannot be null"); + + this.serviceType = serviceType; + this.uriSpec = uriSpec; + this.name = name; + this.id = id; + this.address = address; + this.port = port; + this.sslPort = sslPort; + this.payload = payload; + this.registrationTimeUTC = registrationTimeUTC; + } + + OldServiceInstance() + { + this("", "", null, null, null, null, 0, ServiceType.DYNAMIC, null); + } + + public String getName() + { + return name; + } + + public String getId() + { + return id; + } + + public String getAddress() + { + return address; + } + + public Integer getPort() + { + return port; + } + + public Integer getSslPort() + { + return sslPort; + } + + @JsonTypeInfo(use = Id.CLASS, defaultImpl = Object.class) + public T getPayload() + { + return payload; + } + + public long getRegistrationTimeUTC() + { + return registrationTimeUTC; + } + + public ServiceType getServiceType() + { + return serviceType; + } + + public UriSpec getUriSpec() + { + return uriSpec; + } + + @SuppressWarnings("RedundantIfStatement") + @Override + public boolean equals(Object o) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + OldServiceInstance that = (OldServiceInstance)o; + + if ( registrationTimeUTC != that.registrationTimeUTC ) + { + return false; + } + if ( address != null ? !address.equals(that.address) : that.address != null ) + { + return false; + } + if ( id != null ? !id.equals(that.id) : that.id != null ) + { + return false; + } + if ( name != null ? !name.equals(that.name) : that.name != null ) + { + return false; + } + if ( payload != null ? !payload.equals(that.payload) : that.payload != null ) + { + return false; + } + if ( port != null ? !port.equals(that.port) : that.port != null ) + { + return false; + } + if ( serviceType != that.serviceType ) + { + return false; + } + if ( sslPort != null ? !sslPort.equals(that.sslPort) : that.sslPort != null ) + { + return false; + } + if ( uriSpec != null ? !uriSpec.equals(that.uriSpec) : that.uriSpec != null ) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (id != null ? id.hashCode() : 0); + result = 31 * result + (address != null ? address.hashCode() : 0); + result = 31 * result + (port != null ? port.hashCode() : 0); + result = 31 * result + (sslPort != null ? sslPort.hashCode() : 0); + result = 31 * result + (payload != null ? payload.hashCode() : 0); + result = 31 * result + (int)(registrationTimeUTC ^ (registrationTimeUTC >>> 32)); + result = 31 * result + (serviceType != null ? serviceType.hashCode() : 0); + result = 31 * result + (uriSpec != null ? uriSpec.hashCode() : 0); + return result; + } + + @Override + public String toString() + { + return "ServiceInstance{" + "name='" + name + '\'' + ", id='" + id + '\'' + ", address='" + address + '\'' + ", port=" + port + ", sslPort=" + sslPort + ", payload=" + payload + ", registrationTimeUTC=" + registrationTimeUTC + ", serviceType=" + serviceType + ", uriSpec=" + uriSpec + '}'; + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/070f1c9e/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestJsonInstanceSerializer.java ---------------------------------------------------------------------- diff --git a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestJsonInstanceSerializer.java b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestJsonInstanceSerializer.java index f17919d..ae49dbf 100644 --- a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestJsonInstanceSerializer.java +++ b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestJsonInstanceSerializer.java @@ -16,27 +16,27 @@ * specific language governing permissions and limitations * under the License. */ + package org.apache.curator.x.discovery; +import org.apache.curator.x.discovery.details.JsonInstanceSerializer; +import org.testng.Assert; +import org.testng.annotations.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.curator.x.discovery.details.JsonInstanceSerializer; -import org.testng.Assert; -import org.testng.annotations.Test; - public class TestJsonInstanceSerializer { @Test - public void testBasic() throws Exception + public void testBasic() throws Exception { - JsonInstanceSerializer<String> serializer = new JsonInstanceSerializer<String>(String.class); - ServiceInstance<String> instance = new ServiceInstance<String>("name", "id", "address", 10, 20, "payload", 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); - byte[] bytes = serializer.serialize(instance); + JsonInstanceSerializer<String> serializer = new JsonInstanceSerializer<String>(String.class); + ServiceInstance<String> instance = new ServiceInstance<String>("name", "id", "address", 10, 20, "payload", 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); + byte[] bytes = serializer.serialize(instance); - ServiceInstance<String> rhs = serializer.deserialize(bytes); + ServiceInstance<String> rhs = serializer.deserialize(bytes); Assert.assertEquals(instance, rhs); Assert.assertEquals(instance.getId(), rhs.getId()); Assert.assertEquals(instance.getName(), rhs.getName()); @@ -49,12 +49,12 @@ public class TestJsonInstanceSerializer } @Test - public void testWrongPayloadType() throws Exception + public void testWrongPayloadType() throws Exception { - JsonInstanceSerializer<String> stringSerializer = new JsonInstanceSerializer<String>(String.class); - JsonInstanceSerializer<Double> doubleSerializer = new JsonInstanceSerializer<Double>(Double.class); + JsonInstanceSerializer<String> stringSerializer = new JsonInstanceSerializer<String>(String.class); + JsonInstanceSerializer<Double> doubleSerializer = new JsonInstanceSerializer<Double>(Double.class); - byte[] bytes = stringSerializer.serialize(new ServiceInstance<String>("name", "id", "address", 10, 20, "payload", 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true)); + byte[] bytes = stringSerializer.serialize(new ServiceInstance<String>("name", "id", "address", 10, 20, "payload", 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true)); try { doubleSerializer.deserialize(bytes); @@ -67,13 +67,13 @@ public class TestJsonInstanceSerializer } @Test - public void testNoPayload() throws Exception + public void testNoPayload() throws Exception { - JsonInstanceSerializer<Void> serializer = new JsonInstanceSerializer<Void>(Void.class); - ServiceInstance<Void> instance = new ServiceInstance<Void>("name", "id", "address", 10, 20, null, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); - byte[] bytes = serializer.serialize(instance); + JsonInstanceSerializer<Void> serializer = new JsonInstanceSerializer<Void>(Void.class); + ServiceInstance<Void> instance = new ServiceInstance<Void>("name", "id", "address", 10, 20, null, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); + byte[] bytes = serializer.serialize(instance); - ServiceInstance<Void> rhs = serializer.deserialize(bytes); + ServiceInstance<Void> rhs = serializer.deserialize(bytes); Assert.assertEquals(instance, rhs); Assert.assertEquals(instance.getId(), rhs.getId()); Assert.assertEquals(instance.getName(), rhs.getName()); @@ -86,26 +86,26 @@ public class TestJsonInstanceSerializer } @Test - public void testNoEnabledState() throws Exception + public void testNoEnabledState() throws Exception { - JsonInstanceSerializer<Void> serializer = new JsonInstanceSerializer<Void>(Void.class); - byte[] bytes = "{}".getBytes("utf-8"); + JsonInstanceSerializer<Void> serializer = new JsonInstanceSerializer<Void>(Void.class); + byte[] bytes = "{}".getBytes("utf-8"); - ServiceInstance<Void> instance = serializer.deserialize(bytes); + ServiceInstance<Void> instance = serializer.deserialize(bytes); Assert.assertTrue(instance.isEnabled(), "Instance that has no 'enabled' should be assumed enabled"); } @Test - public void testPayloadAsList() throws Exception + public void testPayloadAsList() throws Exception { - JsonInstanceSerializer<Object> serializer = new JsonInstanceSerializer<Object>(Object.class); + JsonInstanceSerializer<Object> serializer = new JsonInstanceSerializer<Object>(Object.class); List<String> payload = new ArrayList<String>(); payload.add("Test value 1"); payload.add("Test value 2"); - ServiceInstance<Object> instance = new ServiceInstance<Object>("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false); - byte[] bytes = serializer.serialize(instance); + ServiceInstance<Object> instance = new ServiceInstance<Object>("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false); + byte[] bytes = serializer.serialize(instance); - ServiceInstance<Object> rhs = serializer.deserialize(bytes); + ServiceInstance<Object> rhs = serializer.deserialize(bytes); Assert.assertEquals(instance, rhs); Assert.assertEquals(instance.getId(), rhs.getId()); Assert.assertEquals(instance.getName(), rhs.getName()); @@ -117,18 +117,17 @@ public class TestJsonInstanceSerializer Assert.assertEquals(instance.isEnabled(), rhs.isEnabled()); } - @Test - public void testPayloadAsMap() throws Exception + public void testPayloadAsMap() throws Exception { - JsonInstanceSerializer<Object> serializer = new JsonInstanceSerializer<Object>(Object.class); - Map<String,String> payload = new HashMap<String,String>(); + JsonInstanceSerializer<Object> serializer = new JsonInstanceSerializer<Object>(Object.class); + Map<String, String> payload = new HashMap<String, String>(); payload.put("1", "Test value 1"); payload.put("2", "Test value 2"); - ServiceInstance<Object> instance = new ServiceInstance<Object>("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false); - byte[] bytes = serializer.serialize(instance); + ServiceInstance<Object> instance = new ServiceInstance<Object>("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false); + byte[] bytes = serializer.serialize(instance); - ServiceInstance<Object> rhs = serializer.deserialize(bytes); + ServiceInstance<Object> rhs = serializer.deserialize(bytes); Assert.assertEquals(instance, rhs); Assert.assertEquals(instance.getId(), rhs.getId()); Assert.assertEquals(instance.getName(), rhs.getName()); @@ -141,15 +140,15 @@ public class TestJsonInstanceSerializer } @Test - public void testPayloadClass() throws Exception + public void testPayloadClass() throws Exception { - JsonInstanceSerializer<Payload> serializer = new JsonInstanceSerializer<Payload>(Payload.class); + JsonInstanceSerializer<Payload> serializer = new JsonInstanceSerializer<Payload>(Payload.class); Payload payload = new Payload(); payload.setVal("Test value"); - ServiceInstance<Payload> instance = new ServiceInstance<Payload>("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); - byte[] bytes = serializer.serialize(instance); + ServiceInstance<Payload> instance = new ServiceInstance<Payload>("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); + byte[] bytes = serializer.serialize(instance); - ServiceInstance<Payload> rhs = serializer.deserialize(bytes); + ServiceInstance<Payload> rhs = serializer.deserialize(bytes); Assert.assertEquals(instance, rhs); Assert.assertEquals(instance.getId(), rhs.getId()); Assert.assertEquals(instance.getName(), rhs.getName()); @@ -161,21 +160,48 @@ public class TestJsonInstanceSerializer Assert.assertEquals(instance.isEnabled(), rhs.isEnabled()); } - public static class Payload { - private String val; - public String getVal() { - return val; - } - public void setVal(String val) { - this.val = val; - } - @Override - public boolean equals(Object other) { - if (other == null || !(other instanceof Payload)) return false; - String otherVal = ((Payload)other).getVal(); - if (val == null) return val == otherVal; - return val.equals(otherVal); - } - } + public static class Payload + { + private String val; + public Payload(String val) + { + this.val = val; + } + + public Payload() + { + } + + public String getVal() + { + return val; + } + + public void setVal(String val) + { + this.val = val; + } + + @Override + public int hashCode() + { + return val != null ? val.hashCode() : 0; + } + + @Override + public boolean equals(Object other) + { + if ( other == null || !(other instanceof Payload) ) + { + return false; + } + String otherVal = ((Payload)other).getVal(); + if ( val == null ) + { + return val.equals(otherVal); + } + return val.equals(otherVal); + } + } } http://git-wip-us.apache.org/repos/asf/curator/blob/070f1c9e/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/NewServiceInstance.java ---------------------------------------------------------------------- diff --git a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/NewServiceInstance.java b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/NewServiceInstance.java new file mode 100644 index 0000000..407ce6f --- /dev/null +++ b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/NewServiceInstance.java @@ -0,0 +1,148 @@ +/** + * 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.curator.x.discovery.details; + +import com.google.common.base.Preconditions; +import org.apache.curator.x.discovery.ServiceType; +import org.apache.curator.x.discovery.UriSpec; +import org.codehaus.jackson.annotate.JsonTypeInfo; +import org.codehaus.jackson.annotate.JsonTypeInfo.Id; +import java.net.URI; +import java.util.Date; + +/** + * POJO that represents a service instance + */ +class NewServiceInstance<T> +{ + private final String name; + private final String id; + private final String address; + private final Integer port; + private final Integer sslPort; + private final T payload; + private final long registrationTimeUTC; + private final ServiceType serviceType; + private final UriSpec uriSpec; + private final boolean enabled; + private final String new1; + private final Long new2; + private final Date new3; + private final URI new4; + + public NewServiceInstance(String name, String id, String address, Integer port, Integer sslPort, T payload, long registrationTimeUTC, ServiceType serviceType, UriSpec uriSpec, boolean enabled, String new1, Long new2, Date new3, URI new4) + { + name = Preconditions.checkNotNull(name, "name cannot be null"); + id = Preconditions.checkNotNull(id, "id cannot be null"); + + this.new1 = new1; + this.new2 = new2; + this.new3 = new3; + this.new4 = new4; + this.serviceType = serviceType; + this.uriSpec = uriSpec; + this.name = name; + this.id = id; + this.address = address; + this.port = port; + this.sslPort = sslPort; + this.payload = payload; + this.registrationTimeUTC = registrationTimeUTC; + this.enabled = enabled; + } + + /** + * Inits to default values. Only exists for deserialization + */ + NewServiceInstance() + { + this("", "", null, null, null, null, 0, ServiceType.DYNAMIC, null, true, null, null, null, null); + } + + public String getName() + { + return name; + } + + public String getId() + { + return id; + } + + public String getAddress() + { + return address; + } + + public Integer getPort() + { + return port; + } + + public Integer getSslPort() + { + return sslPort; + } + + @JsonTypeInfo(use = Id.CLASS, defaultImpl = Object.class) + public T getPayload() + { + return payload; + } + + public long getRegistrationTimeUTC() + { + return registrationTimeUTC; + } + + public ServiceType getServiceType() + { + return serviceType; + } + + public UriSpec getUriSpec() + { + return uriSpec; + } + + public boolean isEnabled() + { + return enabled; + } + + public String getNew1() + { + return new1; + } + + public Long getNew2() + { + return new2; + } + + public Date getNew3() + { + return new3; + } + + public URI getNew4() + { + return new4; + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/070f1c9e/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestJsonInstanceSerializerCompatibility.java ---------------------------------------------------------------------- diff --git a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestJsonInstanceSerializerCompatibility.java b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestJsonInstanceSerializerCompatibility.java new file mode 100644 index 0000000..c612a1f --- /dev/null +++ b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestJsonInstanceSerializerCompatibility.java @@ -0,0 +1,79 @@ +/** + * 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.curator.x.discovery.details; + +import org.apache.curator.x.discovery.ServiceInstance; +import org.apache.curator.x.discovery.ServiceType; +import org.apache.curator.x.discovery.TestJsonInstanceSerializer; +import org.apache.curator.x.discovery.UriSpec; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.JavaType; +import org.testng.Assert; +import org.testng.annotations.Test; +import java.net.URI; +import java.util.Date; + +public class TestJsonInstanceSerializerCompatibility +{ + @Test + public void testCompatibilityMode() throws Exception + { + JsonInstanceSerializer<TestJsonInstanceSerializer.Payload> serializer = new JsonInstanceSerializer<TestJsonInstanceSerializer.Payload>(TestJsonInstanceSerializer.Payload.class, true, true); + ServiceInstance<TestJsonInstanceSerializer.Payload> instance = new ServiceInstance<TestJsonInstanceSerializer.Payload>("name", "id", "address", 10, 20, new TestJsonInstanceSerializer.Payload("test"), 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); + byte[] bytes = serializer.serialize(instance); + + OldServiceInstance<TestJsonInstanceSerializer.Payload> oldInstance = new OldServiceInstance<TestJsonInstanceSerializer.Payload>("name", "id", "address", 10, 20, new TestJsonInstanceSerializer.Payload("test"), 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}")); + ObjectMapper mapper = new ObjectMapper(); + byte[] oldBytes = mapper.writeValueAsBytes(oldInstance); + Assert.assertEquals(bytes, oldBytes, String.format("%s vs %s", new String(bytes), new String(oldBytes))); + } + + @Test + public void testBackwardCompatibility() throws Exception + { + JsonInstanceSerializer<TestJsonInstanceSerializer.Payload> serializer = new JsonInstanceSerializer<TestJsonInstanceSerializer.Payload>(TestJsonInstanceSerializer.Payload.class, true, true); + ServiceInstance<TestJsonInstanceSerializer.Payload> instance = new ServiceInstance<TestJsonInstanceSerializer.Payload>("name", "id", "address", 10, 20, new TestJsonInstanceSerializer.Payload("test"), 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false); + byte[] bytes = serializer.serialize(instance); + + instance = serializer.deserialize(bytes); + Assert.assertTrue(instance.isEnabled()); // passed false for enabled in the ctor but that is lost with compatibleSerializationMode + + ObjectMapper mapper = new ObjectMapper(); + JavaType type = mapper.getTypeFactory().constructType(OldServiceInstance.class); + OldServiceInstance rawServiceInstance = mapper.readValue(bytes, type); + TestJsonInstanceSerializer.Payload.class.cast(rawServiceInstance.getPayload()); // just to verify that it's the correct type + //noinspection unchecked + OldServiceInstance<TestJsonInstanceSerializer.Payload> check = (OldServiceInstance<TestJsonInstanceSerializer.Payload>)rawServiceInstance; + Assert.assertEquals(check.getName(), "name"); + Assert.assertEquals(check.getPayload(), new TestJsonInstanceSerializer.Payload("test")); + } + + @Test + public void testFutureChanges() throws Exception + { + NewServiceInstance<String> newInstance = new NewServiceInstance<String>("name", "id", "address", 10, 20, "hey", 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false, "what", 10101L, new Date(), new URI("http://hey")); + byte[] newInstanceBytes = new ObjectMapper().writeValueAsBytes(newInstance); + JsonInstanceSerializer<String> serializer = new JsonInstanceSerializer<String>(String.class); + ServiceInstance<String> instance = serializer.deserialize(newInstanceBytes); + Assert.assertEquals(instance.getName(), "name"); + Assert.assertEquals(instance.getPayload(), "hey"); + Assert.assertEquals(instance.isEnabled(), false); + } +}