001: /*
002: * Copyright (C) 2005, 2006 Joe Walnes.
003: * Copyright (C) 2006, 2007, 2008 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 09. December 2005 by Joe Walnes
011: */
012: package com.thoughtworks.acceptance;
013:
014: import com.thoughtworks.acceptance.objects.Software;
015: import com.thoughtworks.acceptance.objects.StandardObject;
016: import com.thoughtworks.xstream.MarshallingStrategy;
017: import com.thoughtworks.xstream.alias.ClassMapper;
018: import com.thoughtworks.xstream.converters.ConverterLookup;
019: import com.thoughtworks.xstream.converters.DataHolder;
020: import com.thoughtworks.xstream.core.DefaultConverterLookup;
021: import com.thoughtworks.xstream.core.ReferenceByIdMarshaller;
022: import com.thoughtworks.xstream.core.ReferenceByIdUnmarshaller;
023: import com.thoughtworks.xstream.io.HierarchicalStreamReader;
024: import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
025: import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
026: import com.thoughtworks.xstream.io.xml.XppReader;
027: import com.thoughtworks.xstream.mapper.Mapper;
028: import com.thoughtworks.xstream.testutil.CallLog;
029:
030: import java.io.ByteArrayInputStream;
031: import java.io.ByteArrayOutputStream;
032: import java.io.EOFException;
033: import java.io.IOException;
034: import java.io.InputStreamReader;
035: import java.io.ObjectInputStream;
036: import java.io.ObjectOutputStream;
037: import java.io.OutputStreamWriter;
038: import java.io.Reader;
039: import java.io.StringReader;
040: import java.io.StringWriter;
041: import java.io.Writer;
042: import java.util.zip.Deflater;
043: import java.util.zip.DeflaterOutputStream;
044: import java.util.zip.Inflater;
045: import java.util.zip.InflaterInputStream;
046:
047: public class MultipleObjectsInOneStreamTest extends
048: AbstractAcceptanceTest {
049:
050: public static class Person extends StandardObject {
051:
052: private String firstName;
053: private String lastName;
054: private Person secretary;
055:
056: public Person(String firstName, String lastName) {
057: this .firstName = firstName;
058: this .lastName = lastName;
059: }
060:
061: }
062:
063: public void testReadAndWriteMultipleObjectsInOneStream() {
064: xstream.alias("person", Person.class);
065: StringWriter buffer = new StringWriter();
066:
067: // serialize
068: HierarchicalStreamWriter writer = new PrettyPrintWriter(buffer);
069: writer.startNode("people");
070: xstream.marshal(new Person("Postman", "Pat"), writer);
071: xstream.marshal(new Person("Bob", "Builder"), writer);
072: xstream.marshal(new Person("Tinky", "Winky"), writer);
073: writer.endNode();
074:
075: assertEquals("" + "<people>\n" + " <person>\n"
076: + " <firstName>Postman</firstName>\n"
077: + " <lastName>Pat</lastName>\n" + " </person>\n"
078: + " <person>\n" + " <firstName>Bob</firstName>\n"
079: + " <lastName>Builder</lastName>\n"
080: + " </person>\n" + " <person>\n"
081: + " <firstName>Tinky</firstName>\n"
082: + " <lastName>Winky</lastName>\n" + " </person>\n"
083: + "</people>", buffer.toString());
084:
085: // deserialize
086: HierarchicalStreamReader reader = new XppReader(
087: new StringReader(buffer.toString()));
088:
089: assertTrue("should be another object to read (1)", reader
090: .hasMoreChildren());
091: reader.moveDown();
092: assertEquals(new Person("Postman", "Pat"), xstream
093: .unmarshal(reader));
094: reader.moveUp();
095:
096: assertTrue("should be another object to read (2)", reader
097: .hasMoreChildren());
098: reader.moveDown();
099: assertEquals(new Person("Bob", "Builder"), xstream
100: .unmarshal(reader));
101: reader.moveUp();
102:
103: assertTrue("should be another object to read (3)", reader
104: .hasMoreChildren());
105: reader.moveDown();
106: assertEquals(new Person("Tinky", "Winky"), xstream
107: .unmarshal(reader));
108: reader.moveUp();
109:
110: assertFalse("should be no more objects", reader
111: .hasMoreChildren());
112: }
113:
114: public void testDrivenThroughObjectStream() throws IOException,
115: ClassNotFoundException {
116: Writer writer = new StringWriter();
117: xstream.alias("software", Software.class);
118:
119: ObjectOutputStream oos = xstream
120: .createObjectOutputStream(writer);
121: oos.writeInt(123);
122: oos.writeObject("hello");
123: oos.writeObject(new Software("tw", "xs"));
124: oos.close();
125:
126: String expectedXml = "" + "<object-stream>\n"
127: + " <int>123</int>\n" + " <string>hello</string>\n"
128: + " <software>\n" + " <vendor>tw</vendor>\n"
129: + " <name>xs</name>\n" + " </software>\n"
130: + "</object-stream>";
131:
132: assertEquals(expectedXml, writer.toString());
133:
134: ObjectInputStream ois = xstream
135: .createObjectInputStream(new StringReader(writer
136: .toString()));
137: assertEquals(123, ois.readInt());
138: assertEquals("hello", ois.readObject());
139: assertEquals(new Software("tw", "xs"), ois.readObject());
140:
141: try {
142: ois.readObject(); // As far as I can see this is the only clue the
143: // ObjectInputStream gives that it's done.
144: fail("Expected EOFException");
145: } catch (EOFException expectedException) {
146: // good
147: }
148: }
149:
150: public void testDrivenThroughCompressedObjectStream()
151: throws IOException, ClassNotFoundException {
152: ByteArrayOutputStream baos = new ByteArrayOutputStream();
153: Writer writer = new OutputStreamWriter(
154: new DeflaterOutputStream(baos, new Deflater(
155: Deflater.BEST_COMPRESSION)), "UTF-8");
156: xstream.alias("software", Software.class);
157:
158: ObjectOutputStream oos = xstream
159: .createObjectOutputStream(writer);
160: oos.writeInt(123);
161: oos.writeObject("hello");
162: oos.writeObject(new Software("tw", "xs"));
163: oos.flush();
164: oos.close();
165:
166: byte[] data = baos.toByteArray();
167: assertTrue("Too less data: " + data.length, data.length > 2);
168:
169: ObjectInputStream ois = xstream
170: .createObjectInputStream(new InputStreamReader(
171: new InflaterInputStream(
172: new ByteArrayInputStream(data),
173: new Inflater()), "UTF-8"));
174: assertEquals(123, ois.readInt());
175: assertEquals("hello", ois.readObject());
176: assertEquals(new Software("tw", "xs"), ois.readObject());
177:
178: try {
179: ois.readObject(); // As far as I can see this is the only clue the
180: // ObjectInputStream gives that it's done.
181: fail("Expected EOFException");
182: } catch (EOFException expectedException) {
183: // good
184: }
185: }
186:
187: public void testObjectOutputStreamPropegatesCloseAndFlushEvents()
188: throws IOException {
189: // setup
190: final CallLog log = new CallLog();
191: Writer loggingWriter = new Writer() {
192: public void close() {
193: log.actual("close");
194: }
195:
196: public void flush() {
197: log.actual("flush");
198: }
199:
200: public void write(char cbuf[], int off, int len) {
201: // don't care about this
202: }
203: };
204:
205: // expectations
206: log.expect("flush"); // TWO flushes are currently caused. Only one is needed, but
207: // this is no big deal.
208: log.expect("flush");
209: log.expect("close");
210:
211: // execute
212: ObjectOutputStream objectOutputStream = xstream
213: .createObjectOutputStream(loggingWriter);
214: objectOutputStream.flush();
215: objectOutputStream.close();
216:
217: // verify
218: log.verify();
219: }
220:
221: public void testObjectInputStreamPropegatesCloseEvent()
222: throws IOException {
223: // setup
224: final CallLog log = new CallLog();
225: Reader loggingReader = new StringReader("<int>1</int>") {
226: public void close() {
227: log.actual("close");
228: }
229: };
230:
231: // expectations
232: log.expect("close");
233:
234: // execute
235: ObjectInputStream objectInputStream = xstream
236: .createObjectInputStream(loggingReader);
237: objectInputStream.close();
238:
239: // verify
240: log.verify();
241: }
242:
243: public void testByDefaultDoesNotPreserveReferencesAcrossDifferentObjectsInStream()
244: throws Exception {
245: xstream.alias("person", Person.class);
246:
247: // Setup initial data: two object, one referencing another...
248: Person alice = new Person("Alice", "Thing");
249: Person jane = new Person("Jane", "Blah");
250: jane.secretary = alice;
251:
252: // Serialize the two individual objects.
253: StringWriter writer = new StringWriter();
254: ObjectOutputStream out = xstream
255: .createObjectOutputStream(writer);
256: out.writeObject(alice);
257: out.writeObject(jane);
258: out.close();
259:
260: // Deserialize the two objects.
261: ObjectInputStream in = xstream
262: .createObjectInputStream(new StringReader(writer
263: .toString()));
264: alice = (Person) in.readObject();
265: jane = (Person) in.readObject();
266: in.close();
267:
268: assertNotSame(alice, jane.secretary); // NOT SAME
269: }
270:
271: static class ReusingReferenceByIdMarshallingStrategy implements
272: MarshallingStrategy {
273:
274: private ReferenceByIdMarshaller marshaller;
275: private ReferenceByIdUnmarshaller unmarshaller;
276:
277: public void marshal(HierarchicalStreamWriter writer,
278: Object obj, ConverterLookup converterLookup,
279: Mapper mapper, DataHolder dataHolder) {
280: if (marshaller == null) {
281: marshaller = new ReferenceByIdMarshaller(writer,
282: converterLookup, mapper);
283: }
284: marshaller.start(obj, dataHolder);
285: }
286:
287: public void marshal(HierarchicalStreamWriter writer,
288: Object obj, DefaultConverterLookup converterLookup,
289: ClassMapper classMapper, DataHolder dataHolder) {
290: throw new UnsupportedOperationException();
291: }
292:
293: public Object unmarshal(Object root,
294: HierarchicalStreamReader reader, DataHolder dataHolder,
295: ConverterLookup converterLookup, Mapper mapper) {
296: if (unmarshaller == null) {
297: unmarshaller = new ReferenceByIdUnmarshaller(root,
298: reader, converterLookup, mapper);
299: }
300: return unmarshaller.start(dataHolder);
301: }
302:
303: public Object unmarshal(Object root,
304: HierarchicalStreamReader reader, DataHolder dataHolder,
305: DefaultConverterLookup converterLookup,
306: ClassMapper classMapper) {
307: throw new UnsupportedOperationException();
308: }
309:
310: }
311:
312: public void testSupportsOptionToPreserveReferencesAcrossDifferentObjectsInStream()
313: throws Exception {
314: xstream.alias("person", Person.class);
315: xstream
316: .setMarshallingStrategy(new ReusingReferenceByIdMarshallingStrategy());
317:
318: // Setup initial data: two object, one referencing another...
319: Person alice = new Person("Alice", "Thing");
320: Person jane = new Person("Jane", "Blah");
321: jane.secretary = alice;
322:
323: // Serialize the two individual objects.
324: StringWriter writer = new StringWriter();
325: ObjectOutputStream out = xstream
326: .createObjectOutputStream(writer);
327: out.writeObject(alice);
328: out.writeObject(jane);
329: out.close();
330:
331: // Deserialize the two objects.
332: ObjectInputStream in = xstream
333: .createObjectInputStream(new StringReader(writer
334: .toString()));
335: alice = (Person) in.readObject();
336: jane = (Person) in.readObject();
337: in.close();
338:
339: assertSame(alice, jane.secretary);
340: }
341: }
|