> > This should work in general. An avro field can be promoted from a String > to a nullable String. The inverse is not true. A client that is expecting > a non-nullable string field response cannot read data serialized with a > nullable string.
What I do in the demo is add a new nullable string in server side, not change a string to nullable string. I add a new field with default value using specific, and it works fine, so I suspect the reason that reflect doesn't work is that I didn't add default value to the nullable string field. Perhaps the default value for nullable field should be null by default? 2011/10/17 Scott Carey <[email protected]> > On 10/13/11 5:25 AM, "常冰琳" <[email protected]> wrote: > > Hi, > I try to use avro-ipc in my project and got a problem, if the server side > needs to upgrade, > add a few optional field to Request/Response object, or add a new method, > thus change the > schema. > I use reflect, add a new String field in Request class with @Nullable in > server side, keep client > side unchanged, but it won't to work. > I think protobuf supports this, just by adding new optional fields. > So does it possible or how to support backward compatibility in other way? > > > > This should work in general. An avro field can be promoted from a String > to a nullable String. The inverse is not true. A client that is expecting > a non-nullable string field response cannot read data serialized with a > nullable string. > > I am not familiar enough with the Protocol requestor or how it interacts > with the Reflect API to spot the issue. I can't tell from the stack trace > if a resolving decoder is in use — which is required to resolve from one > schema to another. > > If you want the server side to evolve you can: > add new fields > remove fields (only if the field has a default on the client side) > promote fields as permitted (int -> long; string -> nullable string; etc.) > rename fields or other named types with aliases. > > Here is the protocol class > > > old: > > > > public interface SampleService { > public static class Request { > int id; > String name; > @Nullable > String address; > > public Request() { > this(0, "null", null); > } > > public Request(int id, String name, String address) { > this.id = id; > this.name = name; > this.address = address; > } > > public int getId() { > return id; > } > > public String getName() { > return name; > } > > public String getAddress() { > return address; > } > > } > > public static class Response { > @Nullable > String value; > > public Response() { > this("null"); > } > public Response(String value) { > this.value = value; > } > > public String getValue() { > return value; > } > } > > int fibonacci(int n); > int sum(int [] nums, int base); > Response complexCall(Request req); > } > > > > > new: > > > public interface SampleService { > public static class Request { > int id; > String name; > @Nullable > String address; > @Nullable > String address2; > > public Request() { > this(0, "null", null, null); > } > > public Request(int id, String name, String address, String address2) { > this.id = id; > this.name = name; > this.address = address; > this.address2 = address2; > } > > public int getId() { > return id; > } > > public String getName() { > return name; > } > > public String getAddress() { > return address; > } > > public String getAddress2() { > return address2; > } > > } > > public static class Response { > @Nullable > String value; > > public Response() { > this("null"); > } > public Response(String value) { > this.value = value; > } > > public String getValue() { > return value; > } > } > > int fibonacci(int n); > int sum(int [] nums, int base); > Response complexCall(Request req); > } > > > Here is the error message: > > Exception in thread "main" org.apache.avro.AvroRuntimeException: > org.apache.avro.AvroTypeException: Found { > "type" : "record", > "name" : "Request", > "namespace" : "com.mycompany.app.SampleService$", > "fields" : [ { > "name" : "id", > "type" : "int" > }, { > "name" : "name", > "type" : "string" > }, { > "name" : "address", > "type" : [ "null", "string" ] > } ] > }, expecting { > "type" : "record", > "name" : "Request", > "namespace" : "com.mycompany.app.SampleService$", > "fields" : [ { > "name" : "id", > "type" : "int" > }, { > "name" : "name", > "type" : "string" > }, { > "name" : "address", > "type" : [ "null", "string" ] > }, { > "name" : "address2", > "type" : [ "null", "string" ] > } ] > } > at > org.apache.avro.ipc.specific.SpecificRequestor.readError(SpecificRequestor.java:126) > at org.apache.avro.ipc.Requestor$Response.getResponse(Requestor.java:555) > at > org.apache.avro.ipc.Requestor$TransceiverCallback.handleResult(Requestor.java:360) > at > org.apache.avro.ipc.Requestor$TransceiverCallback.handleResult(Requestor.java:323) > at > org.apache.avro.ipc.NettyTransceiver$NettyClientAvroHandler.messageReceived(NettyTransceiver.java:382) > at > org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80) > at > org.apache.avro.ipc.NettyTransceiver$NettyClientAvroHandler.handleUpstream(NettyTransceiver.java:364) > at > org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:545) > at > org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:754) > at > org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:302) > at > org.jboss.netty.handler.codec.frame.FrameDecoder.unfoldAndFireMessageReceived(FrameDecoder.java:317) > at > org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:299) > at > org.jboss.netty.handler.codec.frame.FrameDecoder.messageReceived(FrameDecoder.java:216) > at > org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80) > at > org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:545) > at > org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:540) > at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274) > at > org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261) > at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:349) > at > org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:280) > at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:200) > at > org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) > at > org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:44) > at > java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) > at > java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) > at java.lang.Thread.run(Thread.java:680) > >
