• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.interop;
2 
3 import java.io.*;
4 
5 import com.fasterxml.jackson.databind.*;
6 
7 /**
8  * Simple test to ensure that we can make POJOs use Jackson
9  * for JDK serialization, via {@link Externalizable}
10  *
11  * @since 2.1
12  */
13 public class TestExternalizable extends BaseMapTest
14 {
15     /* Not pretty, but needed to make ObjectMapper accessible from
16      * static context (alternatively could use ThreadLocal).
17      */
18     static class MapperHolder {
19         private final ObjectMapper mapper = new ObjectMapper();
20         private final static MapperHolder instance = new MapperHolder();
mapper()21         public static ObjectMapper mapper() { return instance.mapper; }
22     }
23 
24     /**
25      * Helper class we need to adapt {@link ObjectOutput} as
26      * {@link OutputStream}
27      */
28     final static class ExternalizableInput extends InputStream
29     {
30         private final ObjectInput in;
31 
ExternalizableInput(ObjectInput in)32         public ExternalizableInput(ObjectInput in) {
33             this.in = in;
34         }
35 
36         @Override
available()37         public int available() throws IOException {
38             return in.available();
39         }
40 
41         @Override
close()42         public void close() throws IOException {
43             in.close();
44         }
45 
46         @Override
markSupported()47         public boolean  markSupported() {
48             return false;
49         }
50 
51         @Override
read()52         public int read() throws IOException {
53             return in.read();
54         }
55 
56         @Override
read(byte[] buffer)57         public int read(byte[] buffer) throws IOException {
58             return in.read(buffer);
59         }
60 
61         @Override
read(byte[] buffer, int offset, int len)62         public int read(byte[] buffer, int offset, int len) throws IOException {
63             return in.read(buffer, offset, len);
64         }
65 
66         @Override
skip(long n)67         public long skip(long n) throws IOException {
68             return in.skip(n);
69         }
70     }
71 
72     /**
73      * Helper class we need to adapt {@link ObjectOutput} as
74      * {@link OutputStream}
75      */
76     final static class ExternalizableOutput extends OutputStream
77     {
78         private final ObjectOutput out;
79 
ExternalizableOutput(ObjectOutput out)80         public ExternalizableOutput(ObjectOutput out) {
81             this.out = out;
82         }
83 
84         @Override
flush()85         public void flush() throws IOException {
86             out.flush();
87         }
88 
89         @Override
close()90         public void close() throws IOException {
91             out.close();
92         }
93 
94         @Override
write(int ch)95         public void write(int ch) throws IOException {
96             out.write(ch);
97         }
98 
99         @Override
write(byte[] data)100         public void write(byte[] data) throws IOException {
101             out.write(data);
102         }
103 
104         @Override
write(byte[] data, int offset, int len)105         public void write(byte[] data, int offset, int len) throws IOException {
106             out.write(data, offset, len);
107         }
108     }
109 
110 //    @com.fasterxml.jackson.annotation.JsonFormat(shape=com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY)
111     @SuppressWarnings("resource")
112     static class MyPojo implements Externalizable
113     {
114         public int id;
115         public String name;
116         public int[] values;
117 
MyPojo()118         public MyPojo() { } // for deserialization
MyPojo(int id, String name, int[] values)119         public MyPojo(int id, String name, int[] values)
120         {
121             this.id = id;
122             this.name = name;
123             this.values = values;
124         }
125 
126         @Override
readExternal(ObjectInput in)127         public void readExternal(ObjectInput in) throws IOException
128         {
129 //            MapperHolder.mapper().readValue(
130             MapperHolder.mapper().readerForUpdating(this).readValue(new ExternalizableInput(in));
131         }
132 
133         @Override
writeExternal(ObjectOutput oo)134         public void writeExternal(ObjectOutput oo) throws IOException
135         {
136             MapperHolder.mapper().writeValue(new ExternalizableOutput(oo), this);
137         }
138 
139         @Override
equals(Object o)140         public boolean equals(Object o)
141         {
142             if (o == this) return true;
143             if (o == null) return false;
144             if (o.getClass() != getClass()) return false;
145 
146             MyPojo other = (MyPojo) o;
147 
148             if (other.id != id) return false;
149             if (!other.name.equals(name)) return false;
150 
151             if (other.values.length != values.length) return false;
152             for (int i = 0, end = values.length; i < end; ++i) {
153                 if (values[i] != other.values[i]) return false;
154             }
155             return true;
156         }
157     }
158 
159     /*
160     /**********************************************************
161     /* Actual tests
162     /**********************************************************
163      */
164 
165     // Comparison, using JDK native
166     static class MyPojoNative implements Serializable
167     {
168         private static final long serialVersionUID = 1L;
169 
170         public int id;
171         public String name;
172         public int[] values;
173 
MyPojoNative(int id, String name, int[] values)174         public MyPojoNative(int id, String name, int[] values)
175         {
176             this.id = id;
177             this.name = name;
178             this.values = values;
179         }
180     }
181 
182     @SuppressWarnings("unused")
testSerializeAsExternalizable()183     public void testSerializeAsExternalizable() throws Exception
184     {
185         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
186         ObjectOutputStream obs = new ObjectOutputStream(bytes);
187         final MyPojo input = new MyPojo(13, "Foobar", new int[] { 1, 2, 3 } );
188         obs.writeObject(input);
189         obs.close();
190         byte[] ser = bytes.toByteArray();
191 
192         // Ok: just verify it contains stuff it should
193         byte[] json = MapperHolder.mapper().writeValueAsBytes(input);
194 
195         int ix = indexOf(ser, json);
196         if (ix < 0) {
197             fail("Serialization ("+ser.length+") does NOT contain JSON (of "+json.length+")");
198         }
199 
200         // Sanity check:
201         if (false) {
202             bytes = new ByteArrayOutputStream();
203             obs = new ObjectOutputStream(bytes);
204             MyPojoNative p = new MyPojoNative(13, "Foobar", new int[] { 1, 2, 3 } );
205             obs.writeObject(p);
206             obs.close();
207             System.out.println("Native size: "+bytes.size()+", vs JSON: "+ser.length);
208         }
209 
210         // then read back!
211         ObjectInputStream ins = new ObjectInputStream(new ByteArrayInputStream(ser));
212         MyPojo output = (MyPojo) ins.readObject();
213         ins.close();
214         assertNotNull(output);
215 
216         assertEquals(input, output);
217     }
218 
219     /*
220     /**********************************************************
221     /* Helper methods
222     /**********************************************************
223      */
224 
indexOf(byte[] full, byte[] fragment)225     private int indexOf(byte[] full, byte[] fragment)
226     {
227         final byte first = fragment[0];
228         for (int i = 0, end = full.length-fragment.length; i < end; ++i) {
229             if (full[i] != first) continue;
230             if (matches(full, i, fragment)) {
231                 return i;
232             }
233         }
234         return -1;
235     }
236 
matches(byte[] full, int index, byte[] fragment)237     private boolean matches(byte[] full, int index, byte[] fragment)
238     {
239         for (int i = 1, end = fragment.length; i < end; ++i) {
240             if (fragment[i] != full[index+i]) {
241                 return false;
242             }
243         }
244         return true;
245     }
246 }
247