/*******************************************************************************
* 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.wink.server.internal.providers.entity;
import java.io.ByteArrayInputStream;
import java.lang.reflect.Type;
import java.util.StringTokenizer;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import org.apache.wink.common.annotations.Asset;
import org.apache.wink.common.internal.providers.entity.xml.JAXBElementXmlProvider;
import org.apache.wink.common.internal.providers.entity.xml.JAXBXmlProvider;
import org.apache.wink.common.model.atom.AtomFeed;
import org.apache.wink.common.model.atom.ObjectFactory;
import org.apache.wink.server.internal.servlet.MockServletInvocationTest;
import org.apache.wink.test.mock.MockRequestConstructor;
import org.apache.wink.test.mock.TestUtils;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class JAXBElementProviderTest extends MockServletInvocationTest {
private static final String SOURCE_REQUEST =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><feed xmlns:ns2=\"http://a9.com/-/spec/opensearch/1.1/\" xmlns=\"http://www.w3.org/2005/Atom\" ><id>ID</id></feed>";
private static final byte[] SOURCE_REQUEST_BYTES = SOURCE_REQUEST.getBytes();
private static final String SOURCE_RESPONSE =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><feed xmlns:ns2=\"http://a9.com/-/spec/opensearch/1.1/\" xmlns=\"http://www.w3.org/2005/Atom\"><id>ID</id></feed>";
private static final String JAXBXmlRootElement_REQUEST =
"<JAXBXmlRootElement><id>ID</id><name>NAME</name></JAXBXmlRootElement>";
private static final byte[] JAXBXmlRootElement_BYTES = JAXBXmlRootElement_REQUEST.getBytes();
private static final String JAXBXmlRootElement_RESPONSE =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<JAXBXmlRootElement>"
+ "<id>ID</id>"
+ "<name>NAME</name>"
+ "</JAXBXmlRootElement>";
private static final String JAXBXmlType_REQUEST =
"<JAXBXmlType><id>ID</id><name>NAME</name></JAXBXmlType>";
private static final byte[] JAXBXmlType_REQUEST_BYTES = JAXBXmlType_REQUEST.getBytes();
private static final String JAXBXmlType_RESPONSE =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<JAXBXmlType>"
+ "<id>ID</id>"
+ "<name>NAME</name>"
+ "</JAXBXmlType>";
JAXBElement<AtomFeed> atomfeed;
@Override
protected Class<?>[] getClasses() {
return new Class<?>[] {Resource.class};
}
@Path("jaxbresource")
public static class Resource {
@POST
@Path("jaxbelement")
public JAXBElement<AtomFeed> createWraped(JAXBElement<AtomFeed> feed) {
AtomFeed atomFeed = new AtomFeed();
atomFeed.setId(feed.getValue().getId());
JAXBElement<AtomFeed> wrapedFeed = new ObjectFactory().createFeed(atomFeed);
return wrapedFeed;
}
@POST
@Path("xmlrootwithfactory")
public AtomFeed createBare(AtomFeed feed) {
AtomFeed atomFeed = new AtomFeed();
atomFeed.setId(feed.getId());
return atomFeed;
}
@POST
@Path("xmltype")
public JAXBElement<JAXBXmlType> createxmltype(JAXBXmlType resource) {
JAXBXmlType s = new JAXBXmlType();
s.setId(resource.getId());
s.setName(resource.getName());
JAXBElement<JAXBXmlType> element =
new JAXBElement<JAXBXmlType>(new QName("JAXBXmlType"), JAXBXmlType.class, resource);
return element;
}
@POST
@Path("xmlrootnofactory")
public JAXBXmlRootElement createXmlRoot(JAXBXmlRootElement resource) {
JAXBXmlRootElement s = new JAXBXmlRootElement();
s.setId(resource.getId());
s.setName(resource.getName());
return s;
}
@GET
@Path("xmltypenofactory")
public JAXBXmlType getXmltypeNoFactory() {
JAXBXmlType s = new JAXBXmlType();
s.setId("ID");
s.setName("NAME");
return s;
}
@POST
@Path("xmlasset")
public TestAsset getAsset(TestAsset asset) {
return asset;
}
}
@Asset
public static class TestAsset {
JAXBXmlType xml;
@Produces(MediaType.TEXT_XML)
public JAXBXmlType getJAXB() {
return xml;
}
@Consumes(MediaType.TEXT_XML)
public void setJAXB(JAXBXmlType jaxbObject) {
xml = jaxbObject;
}
}
public void testJAXBElementProviderReaderWriter() throws SecurityException,
NoSuchFieldException {
JAXBElementXmlProvider provider = new JAXBElementXmlProvider();
AtomFeed af = new AtomFeed();
org.apache.wink.common.model.atom.ObjectFactory objectFactory =
new org.apache.wink.common.model.atom.ObjectFactory();
JAXBElement<AtomFeed> feed = objectFactory.createFeed(af);
Type genericType =
JAXBElementProviderTest.class.getDeclaredField("atomfeed").getGenericType();
assertTrue(provider.isWriteable(feed.getClass(), genericType, null, new MediaType("text",
"xml")));
assertTrue(provider.isWriteable(feed.getClass(),
genericType,
null,
new MediaType("application", "xml")));
assertTrue(provider.isWriteable(feed.getClass(),
genericType,
null,
new MediaType("application", "atom+xml")));
assertTrue(provider.isWriteable(feed.getClass(),
genericType,
null,
new MediaType("application", "atomsvc+xml")));
assertFalse(provider
.isWriteable(feed.getClass(), genericType, null, new MediaType("text", "plain")));
assertTrue(provider.isReadable(feed.getClass(), genericType, null, new MediaType("text",
"xml")));
assertTrue(provider.isReadable(feed.getClass(),
genericType,
null,
new MediaType("application", "xml")));
assertTrue(provider.isReadable(feed.getClass(),
genericType,
null,
new MediaType("application", "atom+xml")));
assertTrue(provider.isReadable(feed.getClass(),
genericType,
null,
new MediaType("application", "atomsvc+xml")));
assertFalse(provider.isReadable(feed.getClass(), genericType, null, new MediaType("text",
"plain")));
}
@SuppressWarnings("unchecked")
public void testJAXBElementProviderInvocation() throws Exception {
MockHttpServletRequest request =
MockRequestConstructor.constructMockRequest("POST",
"/jaxbresource/jaxbelement",
"text/xml",
"text/xml",
SOURCE_REQUEST_BYTES);
MockHttpServletResponse invoke = invoke(request);
assertEquals(200, invoke.getStatus());
JAXBContext context = JAXBContext.newInstance(AtomFeed.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<AtomFeed> expectedResponse =
(JAXBElement<AtomFeed>)unmarshaller.unmarshal(new ByteArrayInputStream(SOURCE_RESPONSE
.getBytes()));
JAXBElement<AtomFeed> response =
(JAXBElement<AtomFeed>)unmarshaller.unmarshal(new ByteArrayInputStream(invoke
.getContentAsByteArray()));
assertEquals(expectedResponse.getValue().getId(), response.getValue().getId());
}
@SuppressWarnings("unchecked")
public void testJAXBElementProviderInvocationDTD() throws Exception {
String classpath = System.getProperty("java.class.path");
StringTokenizer tokenizer = new StringTokenizer(classpath, System.getProperty("path.separator"));
String path = null;
while (tokenizer.hasMoreTokens()) {
path = tokenizer.nextToken();
if (path.endsWith("test-classes")) {
break;
}
}
/*
* the addition of the DOCTYPE tests a well-known XML parser exploit in which arbitrary server
* filesystem files can be read. See JAXBElementXmlProvider.readFrom
*
*/
final String LOCALSOURCE = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
"<!DOCTYPE data [<!ENTITY file SYSTEM \""+ path +"/etc/JAXBElementProviderTest.txt\">]>" +
"<feed xmlns:ns2=\"http://a9.com/-/spec/opensearch/1.1/\" xmlns=\"http://www.w3.org/2005/Atom\"><id>&file;</id></feed>";
MockHttpServletRequest request =
MockRequestConstructor.constructMockRequest("POST",
"/jaxbresource/jaxbelement",
"text/xml",
"text/xml",
LOCALSOURCE.getBytes());
MockHttpServletResponse invoke = invoke(request);
assertEquals(400, invoke.getStatus()); // 400 means the unmarshaller could not unmarshal the xml
// let's make sure our test string is unmarshallable, just for a good sanity check
JAXBContext testcontext = JAXBContext.newInstance(AtomFeed.class);
Unmarshaller testunmarshaller = testcontext.createUnmarshaller();
JAXBElement<AtomFeed> testresponse =
(JAXBElement<AtomFeed>)testunmarshaller.unmarshal(new ByteArrayInputStream(LOCALSOURCE.getBytes()));
assertEquals("we could not unmarshal the test xml", "YOU SHOULD NOT BE ABLE TO SEE THIS", testresponse.getValue().getId().trim());
}
@SuppressWarnings("unchecked")
public void testJAXBElementProviderInvocationDTDWithCharset() throws Exception {
String classpath = System.getProperty("java.class.path");
StringTokenizer tokenizer = new StringTokenizer(classpath, System.getProperty("path.separator"));
String path = null;
while (tokenizer.hasMoreTokens()) {
path = tokenizer.nextToken();
if (path.endsWith("test-classes")) {
break;
}
}
/*
* the addition of the DOCTYPE tests a well-known XML parser exploit in which arbitrary server
* filesystem files can be read. See JAXBElementXmlProvider.readFrom
*
*/
final String LOCALSOURCE = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
"<!DOCTYPE data [<!ENTITY file SYSTEM \""+ path +"/etc/JAXBElementProviderTest.txt\">]>" +
"<feed xmlns:ns2=\"http://a9.com/-/spec/opensearch/1.1/\" xmlns=\"http://www.w3.org/2005/Atom\"><id>&file;</id></feed>";
MockHttpServletRequest request =
MockRequestConstructor.constructMockRequest("POST",
"/jaxbresource/jaxbelement",
"text/xml",
"text/xml;charset=UTF-8",
LOCALSOURCE.getBytes());
MockHttpServletResponse invoke = invoke(request);
assertEquals(400, invoke.getStatus()); // 400 means the unmarshaller could not unmarshal the xml, this is good
// let's make sure our test string is unmarshallable, just for a good sanity check
JAXBContext testcontext = JAXBContext.newInstance(AtomFeed.class);
Unmarshaller testunmarshaller = testcontext.createUnmarshaller();
JAXBElement<AtomFeed> testresponse =
(JAXBElement<AtomFeed>)testunmarshaller.unmarshal(new ByteArrayInputStream(LOCALSOURCE.getBytes()));
assertEquals("we could not unmarshal the test xml", "YOU SHOULD NOT BE ABLE TO SEE THIS", testresponse.getValue().getId().trim());
}
@SuppressWarnings("unchecked")
public void testJAXBXmlElementProviderInvocation() throws Exception {
MockHttpServletRequest request =
MockRequestConstructor.constructMockRequest("POST",
"/jaxbresource/xmlrootwithfactory",
"text/xml",
"text/xml",
SOURCE_REQUEST_BYTES);
MockHttpServletResponse invoke = invoke(request);
assertEquals(200, invoke.getStatus());
JAXBContext context = JAXBContext.newInstance(AtomFeed.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<AtomFeed> expectedResponse =
(JAXBElement<AtomFeed>)unmarshaller.unmarshal(new ByteArrayInputStream(SOURCE_RESPONSE
.getBytes()));
JAXBElement<AtomFeed> response =
(JAXBElement<AtomFeed>)unmarshaller.unmarshal(new ByteArrayInputStream(invoke
.getContentAsByteArray()));
assertEquals(expectedResponse.getValue().getId(), response.getValue().getId());
}
public void testJAXBXmlElementProviderInvocationXmltypeNoFactory() throws Exception {
MockHttpServletRequest request =
MockRequestConstructor.constructMockRequest("GET",
"/jaxbresource/xmltypenofactory",
"text/xml",
"text/xml",
null);
MockHttpServletResponse invoke = invoke(request);
assertEquals(200, invoke.getStatus());
String msg =
TestUtils.diffIgnoreUpdateWithAttributeQualifier(JAXBXmlType_RESPONSE, invoke
.getContentAsString());
assertNull(msg, msg);
}
public void testJAXBXmlElementProviderInvocationXmltype() throws Exception {
MockHttpServletRequest request =
MockRequestConstructor.constructMockRequest("POST",
"/jaxbresource/xmltype",
"text/xml",
"text/xml",
JAXBXmlType_REQUEST_BYTES);
MockHttpServletResponse invoke = invoke(request);
assertEquals(200, invoke.getStatus());
String msg =
TestUtils.diffIgnoreUpdateWithAttributeQualifier(JAXBXmlType_RESPONSE, invoke
.getContentAsString());
assertNull(msg, msg);
}
public void testJAXBXmlElementProviderInvocationXmlRoot() throws Exception {
MockHttpServletRequest request =
MockRequestConstructor.constructMockRequest("POST",
"/jaxbresource/xmlrootnofactory",
"text/xml",
"text/xml",
JAXBXmlRootElement_BYTES);
MockHttpServletResponse invoke = invoke(request);
assertEquals(200, invoke.getStatus());
String msg =
TestUtils.diffIgnoreUpdateWithAttributeQualifier(JAXBXmlRootElement_RESPONSE, invoke
.getContentAsString());
assertNull(msg, msg);
request =
MockRequestConstructor.constructMockRequest("POST",
"/jaxbresource/xmlrootnofactory",
"text/xml",
"text/xml",
"<BadRequest/>".getBytes());
invoke = invoke(request);
assertEquals(400, invoke.getStatus());
}
public void testJAXBXmlElementProviderInvocationXmlAsset() throws Exception {
MockHttpServletRequest request =
MockRequestConstructor.constructMockRequest("POST",
"/jaxbresource/xmlasset",
"text/xml",
"text/xml",
JAXBXmlType_REQUEST_BYTES);
MockHttpServletResponse invoke = invoke(request);
assertEquals(200, invoke.getStatus());
String msg =
TestUtils.diffIgnoreUpdateWithAttributeQualifier(JAXBXmlType_RESPONSE, invoke
.getContentAsString());
assertNull(msg, msg);
}
public void testJAXBXmlElementProviderReaderWriter() throws SecurityException,
NoSuchFieldException {
JAXBXmlProvider provider = new JAXBXmlProvider();
AtomFeed af = new AtomFeed();
assertTrue(provider.isWriteable(af.getClass(), null, null, new MediaType("text", "xml")));
assertTrue(provider.isWriteable(af.getClass(), null, null, new MediaType("application",
"xml")));
assertTrue(provider.isWriteable(af.getClass(), null, null, new MediaType("application",
"atom+xml")));
assertTrue(provider.isWriteable(af.getClass(), null, null, new MediaType("application",
"atomsvc+xml")));
assertFalse(provider.isWriteable(af.getClass(), null, null, new MediaType("text", "plain")));
assertTrue(provider.isReadable(af.getClass(), null, null, new MediaType("text", "xml")));
assertTrue(provider.isReadable(af.getClass(), null, null, new MediaType("application",
"xml")));
assertTrue(provider.isReadable(af.getClass(), null, null, new MediaType("application",
"atom+xml")));
assertTrue(provider.isReadable(af.getClass(), null, null, new MediaType("application",
"atomsvc+xml")));
assertFalse(provider.isReadable(af.getClass(), null, null, new MediaType("text", "plain")));
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "JAXBXmlType", propOrder = {"id", "name"})
public static class JAXBXmlType {
@XmlElement(required = true)
private String id;
@XmlElement(required = true)
private String name;
public JAXBXmlType() {
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "JAXBXmlRootElement", propOrder = {"id", "name"})
@XmlRootElement(name = "JAXBXmlRootElement")
public static class JAXBXmlRootElement {
@XmlElement(required = true)
private String id;
@XmlElement(required = true)
private String name;
public JAXBXmlRootElement() {
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}