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);
+    }
+}

Reply via email to