1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.oxm.jaxb;
18
19 import org.custommonkey.xmlunit.XMLTestCase;
20 import static org.easymock.EasyMock.*;
21 import org.springframework.core.io.ClassPathResource;
22 import org.springframework.core.io.Resource;
23 import org.springframework.oxm.XmlMappingException;
24 import org.springframework.oxm.jaxb2.FlightType;
25 import org.springframework.oxm.jaxb2.Flights;
26 import org.springframework.oxm.jaxb2.ObjectFactory;
27 import org.springframework.oxm.mime.MimeContainer;
28 import org.springframework.util.FileCopyUtils;
29 import org.springframework.xml.transform.StaxResult;
30 import org.springframework.xml.transform.StringResult;
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Element;
33 import org.w3c.dom.Text;
34 import org.xml.sax.Attributes;
35 import org.xml.sax.ContentHandler;
36 import org.xml.sax.Locator;
37
38 import javax.activation.DataHandler;
39 import javax.activation.FileDataSource;
40 import javax.xml.bind.JAXBElement;
41 import javax.xml.datatype.Duration;
42 import javax.xml.datatype.XMLGregorianCalendar;
43 import javax.xml.namespace.QName;
44 import javax.xml.parsers.DocumentBuilder;
45 import javax.xml.parsers.DocumentBuilderFactory;
46 import javax.xml.stream.XMLEventWriter;
47 import javax.xml.stream.XMLOutputFactory;
48 import javax.xml.stream.XMLStreamWriter;
49 import javax.xml.transform.Result;
50 import javax.xml.transform.Source;
51 import javax.xml.transform.dom.DOMResult;
52 import javax.xml.transform.sax.SAXResult;
53 import javax.xml.transform.stream.StreamResult;
54 import java.awt.*;
55 import java.io.ByteArrayOutputStream;
56 import java.io.StringWriter;
57 import java.lang.reflect.Method;
58 import java.lang.reflect.ParameterizedType;
59 import java.lang.reflect.Type;
60 import java.math.BigDecimal;
61 import java.math.BigInteger;
62 import java.net.URI;
63 import java.util.Calendar;
64 import java.util.Collections;
65 import java.util.Date;
66 import java.util.UUID;
67
68 public class Jaxb2MarshallerTest extends XMLTestCase {
69
70 private static final String CONTEXT_PATH = "org.springframework.oxm.jaxb2";
71
72 private static final String EXPECTED_STRING =
73 "<tns:flights xmlns:tns=\"http://samples.springframework.org/flight\">" +
74 "<tns:flight><tns:number>42</tns:number></tns:flight></tns:flights>";
75
76 private Jaxb2Marshaller marshaller;
77
78 private Flights flights;
79
80 protected void setUp() throws Exception {
81 marshaller = new Jaxb2Marshaller();
82 marshaller.setContextPath(CONTEXT_PATH);
83 marshaller.afterPropertiesSet();
84 FlightType flight = new FlightType();
85 flight.setNumber(42L);
86 flights = new Flights();
87 flights.getFlight().add(flight);
88 }
89
90 public void testMarshalDOMResult() throws Exception {
91 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
92 DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
93 Document document = builder.newDocument();
94 DOMResult domResult = new DOMResult(document);
95 marshaller.marshal(flights, domResult);
96 Document expected = builder.newDocument();
97 Element flightsElement = expected.createElementNS("http://samples.springframework.org/flight", "tns:flights");
98 expected.appendChild(flightsElement);
99 Element flightElement = expected.createElementNS("http://samples.springframework.org/flight", "tns:flight");
100 flightsElement.appendChild(flightElement);
101 Element numberElement = expected.createElementNS("http://samples.springframework.org/flight", "tns:number");
102 flightElement.appendChild(numberElement);
103 Text text = expected.createTextNode("42");
104 numberElement.appendChild(text);
105 assertXMLEqual("Marshaller writes invalid DOMResult", expected, document);
106 }
107
108 public void testMarshalStreamResultWriter() throws Exception {
109 StringWriter writer = new StringWriter();
110 StreamResult result = new StreamResult(writer);
111 marshaller.marshal(flights, result);
112 assertXMLEqual("Marshaller writes invalid StreamResult", EXPECTED_STRING, writer.toString());
113 }
114
115 public void testMarshalStreamResultOutputStream() throws Exception {
116 ByteArrayOutputStream os = new ByteArrayOutputStream();
117 StreamResult result = new StreamResult(os);
118 marshaller.marshal(flights, result);
119 assertXMLEqual("Marshaller writes invalid StreamResult", EXPECTED_STRING,
120 new String(os.toByteArray(), "UTF-8"));
121 }
122
123 public void testMarshalStaxResultXMLStreamWriter() throws Exception {
124 XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
125 StringWriter writer = new StringWriter();
126 XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(writer);
127 StaxResult result = new StaxResult(streamWriter);
128 marshaller.marshal(flights, result);
129 assertXMLEqual("Marshaller writes invalid StreamResult", EXPECTED_STRING, writer.toString());
130 }
131
132 public void testMarshalStaxResultXMLEventWriter() throws Exception {
133 XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
134 StringWriter writer = new StringWriter();
135 XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(writer);
136 StaxResult result = new StaxResult(eventWriter);
137 marshaller.marshal(flights, result);
138 assertXMLEqual("Marshaller writes invalid StreamResult", EXPECTED_STRING, writer.toString());
139 }
140
141 public void testProperties() throws Exception {
142 Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
143 marshaller.setContextPath(CONTEXT_PATH);
144 marshaller.setMarshallerProperties(
145 Collections.singletonMap(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE));
146 marshaller.afterPropertiesSet();
147 }
148
149 public void testNoContextPathOrClassesToBeBound() throws Exception {
150 try {
151 Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
152 marshaller.afterPropertiesSet();
153 fail("Should have thrown an IllegalArgumentException");
154 }
155 catch (IllegalArgumentException e) {
156 }
157 }
158
159 public void testInvalidContextPath() throws Exception {
160 try {
161 Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
162 marshaller.setContextPath("ab");
163 marshaller.afterPropertiesSet();
164 fail("Should have thrown an XmlMappingException");
165 }
166 catch (XmlMappingException ex) {
167 }
168 }
169
170 public void testMarshalInvalidClass() throws Exception {
171 Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
172 marshaller.setClassesToBeBound(new Class[]{FlightType.class});
173 marshaller.afterPropertiesSet();
174 Result result = new StreamResult(new StringWriter());
175 Flights flights = new Flights();
176 try {
177 marshaller.marshal(flights, result);
178 fail("Should have thrown an MarshallingFailureException");
179 }
180 catch (XmlMappingException ex) {
181
182 }
183 }
184
185 public void testMarshalSaxResult() throws Exception {
186 ContentHandler handlerMock = createStrictMock(ContentHandler.class);
187 handlerMock.setDocumentLocator(isA(Locator.class));
188 handlerMock.startDocument();
189 handlerMock.startPrefixMapping("", "http://samples.springframework.org/flight");
190 handlerMock.startElement(eq("http://samples.springframework.org/flight"), eq("flights"), eq("flights"),
191 isA(Attributes.class));
192 handlerMock.startElement(eq("http://samples.springframework.org/flight"), eq("flight"), eq("flight"),
193 isA(Attributes.class));
194 handlerMock.startElement(eq("http://samples.springframework.org/flight"), eq("number"), eq("number"),
195 isA(Attributes.class));
196 handlerMock.characters(isA(char[].class), eq(0), eq(2));
197 handlerMock.endElement("http://samples.springframework.org/flight", "number", "number");
198 handlerMock.endElement("http://samples.springframework.org/flight", "flight", "flight");
199 handlerMock.endElement("http://samples.springframework.org/flight", "flights", "flights");
200 handlerMock.endPrefixMapping("");
201 handlerMock.endDocument();
202 replay(handlerMock);
203
204 SAXResult result = new SAXResult(handlerMock);
205 marshaller.marshal(flights, result);
206 verify(handlerMock);
207 }
208
209 public void testSupportsContextPath() throws Exception {
210 Method createFlights = ObjectFactory.class.getDeclaredMethod("createFlights");
211 assertTrue("Jaxb2Marshaller does not support Flights",
212 marshaller.supports(createFlights.getGenericReturnType()));
213 Method createFlight = ObjectFactory.class.getDeclaredMethod("createFlight", FlightType.class);
214 assertTrue("Jaxb2Marshaller does not support JAXBElement<FlightsType>",
215 marshaller.supports(createFlight.getGenericReturnType()));
216 assertFalse("Jaxb2Marshaller supports non-parameterized JAXBElement", marshaller.supports(JAXBElement.class));
217 JAXBElement<Jaxb2MarshallerTest> testElement =
218 new JAXBElement<Jaxb2MarshallerTest>(new QName("something"), Jaxb2MarshallerTest.class, null, this);
219 assertFalse("Jaxb2Marshaller supports wrong JAXBElement", marshaller.supports(testElement.getClass()));
220 }
221
222 public void testSupportsClassesToBeBound() throws Exception {
223 marshaller = new Jaxb2Marshaller();
224 marshaller.setClassesToBeBound(new Class[]{Flights.class, FlightType.class});
225 marshaller.afterPropertiesSet();
226 Method createFlights = ObjectFactory.class.getDeclaredMethod("createFlights");
227 assertTrue("Jaxb2Marshaller does not support Flights",
228 marshaller.supports(createFlights.getGenericReturnType()));
229 Method createFlight = ObjectFactory.class.getDeclaredMethod("createFlight", FlightType.class);
230 assertTrue("Jaxb2Marshaller does not support JAXBElement<FlightsType>",
231 marshaller.supports(createFlight.getGenericReturnType()));
232 assertFalse("Jaxb2Marshaller supports non-parameterized JAXBElement", marshaller.supports(JAXBElement.class));
233 JAXBElement<Jaxb2MarshallerTest> testElement =
234 new JAXBElement<Jaxb2MarshallerTest>(new QName("something"), Jaxb2MarshallerTest.class, null, this);
235 assertFalse("Jaxb2Marshaller supports wrong JAXBElement", marshaller.supports(testElement.getClass()));
236 }
237
238 public void testSupportsPrimitives() throws Exception {
239 Method primitives = getClass().getDeclaredMethod("primitives", JAXBElement.class, JAXBElement.class, JAXBElement.class,
240 JAXBElement.class, JAXBElement.class, JAXBElement.class, JAXBElement.class, JAXBElement.class);
241 Type[] types = primitives.getGenericParameterTypes();
242 for (int i = 0; i < types.length; i++) {
243 ParameterizedType type = (ParameterizedType) types[i];
244 assertTrue("Jaxb2Marshaller does not support " + type, marshaller.supports(types[i]));
245 }
246 }
247
248 public void testSupportsStandards() throws Exception {
249 Method standards = getClass().getDeclaredMethod("standards", JAXBElement.class, JAXBElement.class, JAXBElement.class,
250 JAXBElement.class, JAXBElement.class, JAXBElement.class, JAXBElement.class, JAXBElement.class, JAXBElement.class, JAXBElement.class,
251 JAXBElement.class, JAXBElement.class, JAXBElement.class, JAXBElement.class);
252 Type[] types = standards.getGenericParameterTypes();
253 for (int i = 0; i < types.length; i++) {
254 ParameterizedType type = (ParameterizedType) types[i];
255 assertTrue("Jaxb2Marshaller does not support " + type, marshaller.supports(types[i]));
256 }
257 }
258
259 public void testMarshalAttachments() throws Exception {
260 marshaller = new Jaxb2Marshaller();
261 marshaller.setClassesToBeBound(new Class[]{BinaryObject.class});
262 marshaller.setMtomEnabled(true);
263 marshaller.afterPropertiesSet();
264 MimeContainer mimeContainer = createMock(MimeContainer.class);
265
266 Resource logo = new ClassPathResource("spring-ws.png", getClass());
267 DataHandler dataHandler = new DataHandler(new FileDataSource(logo.getFile()));
268
269 expect(mimeContainer.convertToXopPackage()).andReturn(true);
270 mimeContainer.addAttachment(isA(String.class), isA(DataHandler.class));
271 expectLastCall().times(3);
272
273 replay(mimeContainer);
274 byte[] bytes = FileCopyUtils.copyToByteArray(logo.getInputStream());
275 BinaryObject object = new BinaryObject(bytes, dataHandler);
276 Result result = new StringResult();
277 marshaller.marshal(object, result, mimeContainer);
278 verify(mimeContainer);
279 assertTrue("No XML written", result.toString().length() > 0);
280 }
281
282 private void primitives(JAXBElement<Boolean> bool, JAXBElement<Byte> aByte, JAXBElement<Short> aShort,
283 JAXBElement<Integer> anInteger, JAXBElement<Long> aLong, JAXBElement<Float> aFloat,
284 JAXBElement<Double> aDouble, JAXBElement<byte[]> byteArray) {
285 }
286
287 private void standards(JAXBElement<String> string, JAXBElement<BigInteger> integer, JAXBElement<BigDecimal> decimal,
288 JAXBElement<Calendar> calendar, JAXBElement<Date> date, JAXBElement<QName> qName,
289 JAXBElement<URI> uri, JAXBElement<XMLGregorianCalendar> xmlGregorianCalendar,
290 JAXBElement<Duration> duration, JAXBElement<Object> object, JAXBElement<Image> image,
291 JAXBElement<DataHandler> dataHandler, JAXBElement<Source> source, JAXBElement<UUID> uuid) {
292 }
293 }