/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * http://glassfish.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package org.glassfish.jersey.tests.e2e.server.wadl; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ExecutionException; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.MatrixParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Application; import javax.ws.rs.core.Context; import javax.ws.rs.core.Form; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import javax.inject.Named; import javax.xml.XMLConstants; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.glassfish.jersey.internal.MapPropertiesDelegate; import org.glassfish.jersey.internal.util.SaxHelper; import org.glassfish.jersey.internal.util.SimpleNamespaceResolver; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.message.internal.MediaTypes; import org.glassfish.jersey.server.ApplicationHandler; import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.ContainerResponse; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ServerProperties; import org.glassfish.jersey.server.wadl.WadlApplicationContext; import org.glassfish.jersey.server.wadl.WadlGenerator; import org.glassfish.jersey.server.wadl.config.WadlGeneratorConfig; import org.glassfish.jersey.server.wadl.config.WadlGeneratorDescription; import org.glassfish.jersey.server.wadl.internal.WadlGeneratorImpl; import org.glassfish.jersey.server.wadl.internal.WadlUtils; import org.glassfish.jersey.test.JerseyTest; import org.glassfish.jersey.test.TestProperties; import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.ElementQualifier; import org.custommonkey.xmlunit.SimpleNamespaceContext; import org.custommonkey.xmlunit.XMLAssert; import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableMap; import com.sun.research.ws.wadl.Method; import com.sun.research.ws.wadl.Param; import com.sun.research.ws.wadl.Request; import com.sun.research.ws.wadl.Resource; import com.sun.research.ws.wadl.Resources; /** * WADL use case tests. * * @author Marc Hadley * @author Miroslav Fuksa * @author Michal Gajdos * @author Libor Kramolis (libor.kramolis at oracle.com) * @author Marek Potociar (marek.potociar at oracle.com) */ @RunWith(Suite.class) @Suite.SuiteClasses({ WadlResourceTest.Wadl1Test.class, WadlResourceTest.Wadl2Test.class, WadlResourceTest.Wadl3Test.class, WadlResourceTest.Wadl4Test.class, WadlResourceTest.Wadl5Test.class, WadlResourceTest.Wadl6Test.class, WadlResourceTest.Wadl7Test.class, WadlResourceTest.Wadl8Test.class, WadlResourceTest.Wadl9Test.class, WadlResourceTest.Wadl10Test.class, WadlResourceTest.Wadl11Test.class, }) public class WadlResourceTest { /** * Extracts WADL as {@link Document} from given {@link Response}. * * @param response The response to extract {@code Document} from. * @return The extracted {@code Document}. * @throws ParserConfigurationException In case of parser configuration issues. * @throws SAXException In case of parsing issues. * @throws IOException In case of IO error. */ static Document extractWadlAsDocument(final Response response) throws ParserConfigurationException, SAXException, IOException { assertEquals(200, response.getStatus()); final File tmpFile = response.readEntity(File.class); final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); bf.setValidating(false); if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) { bf.setXIncludeAware(false); } final DocumentBuilder b = bf.newDocumentBuilder(); final Document d = b.parse(tmpFile); Wadl5Test.printSource(new DOMSource(d)); return d; } private static String nodeAsString(final Object resourceNode) throws TransformerException { StringWriter writer = new StringWriter(); Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.transform(new DOMSource((Node) resourceNode), new StreamResult(writer)); return writer.toString(); } public static class Wadl7Test extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(TestRootResource.class); } @Path("root") public static class TestRootResource { @Path("{template}") @GET public String getSub() { return "get"; } @GET public String get() { return "getroot"; } } @Test public void testPathTemplateInSubResourceMethod() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { final Response response = target("root/foo").request(MediaTypes.WADL_TYPE).options(); assertEquals(200, response.getStatus()); } @Test public void testPathTemplateInSubResourceMethod2() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { final Response response = target("root").request(MediaTypes.WADL_TYPE).options(); assertEquals(200, response.getStatus()); } } public static class Wadl5Test extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(WidgetsResource.class, ExtraResource.class); } @Path("foo") public static class ExtraResource { @GET @Produces("application/xml") public String getRep() { return null; } } @Path("widgets") public static class WidgetsResource { @GET @Produces({"application/xml", "application/json"}) public String getWidgets() { return null; } @POST @Consumes({"application/xml"}) @Produces({"application/xml", "application/json"}) public String createWidget(final String bar, @MatrixParam("test") final String test) { return bar; } @PUT @Path("{id}") @Consumes("application/xml") public void updateWidget(final String bar, @PathParam("id") final int id) { } @GET @Path("{id}") @Produces({"application/xml", "application/json"}) public String getWidget(@PathParam("id") final int id) { return null; } @DELETE @Path("{id}") public void deleteWidget(@PathParam("id") final int id) { } @Path("{id}/verbose") public ExtraResource getVerbose(@PathParam("id") final int id) { return new ExtraResource(); } } @Test public void testDisableWadl() throws ExecutionException, InterruptedException { final ResourceConfig rc = new ResourceConfig(WidgetsResource.class, ExtraResource.class); rc.property(ServerProperties.WADL_FEATURE_DISABLE, true); final ApplicationHandler applicationHandler = new ApplicationHandler(rc); final ContainerResponse containerResponse = applicationHandler.apply(new ContainerRequest( URI.create("/"), URI.create("/application.wadl"), "GET", null, new MapPropertiesDelegate())).get(); assertEquals(404, containerResponse.getStatus()); } @Test public void testEnableWadl() throws ExecutionException, InterruptedException { final ResourceConfig rc = new ResourceConfig(WidgetsResource.class, ExtraResource.class); rc.property(ServerProperties.WADL_FEATURE_DISABLE, false); final ApplicationHandler applicationHandler = new ApplicationHandler(rc); final ContainerResponse containerResponse = applicationHandler.apply(new ContainerRequest( URI.create("/"), URI.create("/application.wadl"), "GET", null, new MapPropertiesDelegate())).get(); assertEquals(200, containerResponse.getStatus()); } public static void printSource(final Source source) { try { System.out.println("---------------------"); final Transformer trans = TransformerFactory.newInstance().newTransformer(); final Properties oprops = new Properties(); oprops.put(OutputKeys.OMIT_XML_DECLARATION, "yes"); oprops.put(OutputKeys.INDENT, "yes"); oprops.put(OutputKeys.METHOD, "xml"); trans.setOutputProperties(oprops); trans.transform(source, new StreamResult(System.out)); } catch (final Exception e) { e.printStackTrace(); } } public static String getXmlSchemaNamespacePrefix(final Node elementNode) { final NamedNodeMap attributes = elementNode.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { final Node item = attributes.item(i); if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(item.getNodeValue())) { return item.getLocalName(); } } return "xs"; } /** * Test WADL generation. * * @throws Exception in case of unexpected test failure. */ @Test public void testGetWadl() throws Exception { final File tmpFile = target("application.wadl").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true") .request().get(File.class); final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); bf.setValidating(false); if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) { bf.setXIncludeAware(false); } final DocumentBuilder b = bf.newDocumentBuilder(); final Document d = b.parse(tmpFile); printSource(new DOMSource(d)); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); // check base URI String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val, getBaseUri().toString()); // check total number of resources is 4 val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING); assertEquals("6", val); // check only once resource with for {id} val = (String) xp.evaluate("count(//wadl:resource[@path='{id}'])", d, XPathConstants.STRING); assertEquals("1", val); // check only once resource with for {id}/verbose val = (String) xp.evaluate("count(//wadl:resource[@path='{id}/verbose'])", d, XPathConstants.STRING); assertEquals("1", val); // check only once resource with for widgets val = (String) xp.evaluate("count(//wadl:resource[@path='widgets'])", d, XPathConstants.STRING); assertEquals("1", val); // check 3 methods for {id} val = (String) xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method)", d, XPathConstants.STRING); assertEquals("6", val); // check 2 methods for widgets val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method)", d, XPathConstants.STRING); assertEquals("5", val); // check 1 matrix param on resource val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:param[@name='test'])", d, XPathConstants.STRING); assertEquals("1", val); // check type of {id} is int String prefix = getXmlSchemaNamespacePrefix( (Node) xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']", d, XPathConstants.NODE)); val = (String) xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']/@type", d, XPathConstants.STRING); assertEquals(val, (prefix == null ? "" : prefix + ":") + "int"); // check number of output representations is two val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method[@name='GET']/wadl:response/wadl" + ":representation)", d, XPathConstants.STRING); assertEquals("2", val); // check number of output representations is one val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method[@name='POST']/wadl:request/wadl" + ":representation)", d, XPathConstants.STRING); assertEquals("1", val); // check type of {id}/verbose is int prefix = getXmlSchemaNamespacePrefix( (Node) xp.evaluate("//wadl:resource[@path='{id}/verbose']/wadl:param[@name='id']", d, XPathConstants.NODE)); val = (String) xp.evaluate("//wadl:resource[@path='{id}/verbose']/wadl:param[@name='id']/@type", d, XPathConstants.STRING); assertEquals(val, (prefix == null ? "" : prefix + ":") + "int"); } @Test public void testLastModifiedGET() { final WebTarget target = target("/application.wadl"); final Response r = target.queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request().get(Response.class); assertTrue(r.getHeaders().containsKey("Last-modified")); } @Test public void testLastModifiedOPTIONS() { final WebTarget target = target("/widgets/3/verbose"); final Response r = target.queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request(MediaTypes.WADL_TYPE) .options(); System.out.println(r.readEntity(String.class)); assertTrue(r.getHeaders().containsKey("Last-modified")); } @Test public void testOptionsResourceWadl() throws ParserConfigurationException, XPathExpressionException, IOException, SAXException { // test WidgetsResource Response response = target("/widgets").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true") .request(MediaTypes.WADL_TYPE).options(); assertEquals(200, response.getStatus()); File tmpFile = response.readEntity(File.class); final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); bf.setValidating(false); if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) { bf.setXIncludeAware(false); } DocumentBuilder b = bf.newDocumentBuilder(); Document d = b.parse(tmpFile); printSource(new DOMSource(d)); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); // check base URI String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val, getBaseUri().toString()); // check total number of resources is 3 (no ExtraResource details included) val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING); assertEquals("3", val); // check only once resource with for {id} val = (String) xp.evaluate("count(//wadl:resource[@path='{id}'])", d, XPathConstants.STRING); assertEquals("1", val); // check only once resource with for {id}/verbose val = (String) xp.evaluate("count(//wadl:resource[@path='{id}/verbose'])", d, XPathConstants.STRING); assertEquals("1", val); // check only once resource with for widgets val = (String) xp.evaluate("count(//wadl:resource[@path='widgets'])", d, XPathConstants.STRING); assertEquals("1", val); // check 3 methods for {id} val = (String) xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method)", d, XPathConstants.STRING); assertEquals("6", val); // check 2 methods for widgets val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method)", d, XPathConstants.STRING); assertEquals("5", val); // check type of {id} is int final String prefix = getXmlSchemaNamespacePrefix( (Node) xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']", d, XPathConstants.NODE)); val = (String) xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']/@type", d, XPathConstants.STRING); assertEquals(val, (prefix == null ? "" : prefix + ":") + "int"); // check number of output representations is two val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method[@name='GET']/wadl:response/wadl" + ":representation)", d, XPathConstants.STRING); assertEquals("2", val); // check number of output representations is one val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method[@name='POST']/wadl:request/wadl" + ":representation)", d, XPathConstants.STRING); assertEquals("1", val); response = target("/foo").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true") .request(MediaTypes.WADL_TYPE).options(); assertEquals(200, response.getStatus()); tmpFile = response.readEntity(File.class); b = bf.newDocumentBuilder(); d = b.parse(tmpFile); printSource(new DOMSource(d)); // check base URI val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val, getBaseUri().toString()); // check total number of resources is 1 (no ExtraResource details included) val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING); assertEquals("1", val); // check only once resource with path foo val = (String) xp.evaluate("count(//wadl:resource[@path='foo'])", d, XPathConstants.STRING); assertEquals("1", val); // check 1 methods for foo val = (String) xp.evaluate("count(//wadl:resource[@path='foo']/wadl:method)", d, XPathConstants.STRING); assertEquals("4", val); } @Test public void testOptionsLocatorWadl() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { // test WidgetsResource final File tmpFile = target("/widgets/3/verbose").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true") .request(MediaTypes.WADL_TYPE).options(File.class); final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); bf.setValidating(false); if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) { bf.setXIncludeAware(false); } final DocumentBuilder b = bf.newDocumentBuilder(); final Document d = b.parse(tmpFile); printSource(new DOMSource(d)); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); // check base URI String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val, getBaseUri().toString()); // check total number of resources is 1 (no ExtraResource details included) val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING); assertEquals(val, "1"); // check only once resource with path val = (String) xp.evaluate("count(//wadl:resource[@path='widgets/3/verbose'])", d, XPathConstants.STRING); assertEquals(val, "1"); // check 1 methods val = (String) xp.evaluate("count(//wadl:resource[@path='widgets/3/verbose']/wadl:method)", d, XPathConstants.STRING); assertEquals("4", val); } @Test public void testOptionsSubResourceWadl() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { // test WidgetsResource final File tmpFile = target("/widgets/3").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true") .request(MediaTypes.WADL_TYPE).options(File.class); final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); bf.setValidating(false); if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) { bf.setXIncludeAware(false); } final DocumentBuilder b = bf.newDocumentBuilder(); final Document d = b.parse(tmpFile); printSource(new DOMSource(d)); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val, getBaseUri().toString()); // check total number of resources is 1 val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING); assertEquals("1", val); // check only one resource with for {id} val = (String) xp.evaluate("count(//wadl:resource[@path='widgets/3'])", d, XPathConstants.STRING); assertEquals("1", val); // check 3 methods val = (String) xp.evaluate("count(//wadl:resource[@path='widgets/3']/wadl:method)", d, XPathConstants.STRING); assertEquals("6", val); } public static class MyWadlGeneratorConfig extends WadlGeneratorConfig { @Override public List<WadlGeneratorDescription> configure() { return generator(MyWadlGenerator.class).descriptions(); } public static class MyWadlGenerator extends WadlGeneratorImpl { public static final String CUSTOM_RESOURCES_BASE_URI = "http://myBaseUri"; @Override public Resources createResources() { final Resources resources = super.createResources(); resources.setBase(CUSTOM_RESOURCES_BASE_URI); return resources; } @Override public void setWadlGeneratorDelegate(final WadlGenerator delegate) { // nothing } } } /** * Test overriding WADL's /application/resources/@base attribute. * * @throws Exception in case of unexpected test failure. */ @Test public void testCustomWadlResourcesBaseUri() throws Exception { final ResourceConfig rc = new ResourceConfig(WidgetsResource.class, ExtraResource.class); rc.property(ServerProperties.WADL_GENERATOR_CONFIG, MyWadlGeneratorConfig.class.getName()); final ApplicationHandler applicationHandler = new ApplicationHandler(rc); final ContainerResponse containerResponse = applicationHandler.apply(new ContainerRequest( URI.create("/"), URI.create("/application.wadl"), "GET", null, new MapPropertiesDelegate())).get(); final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); bf.setValidating(false); if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) { bf.setXIncludeAware(false); } final DocumentBuilder b = bf.newDocumentBuilder(); ((ByteArrayInputStream) containerResponse.getEntity()).reset(); final Document d = b.parse((InputStream) containerResponse.getEntity()); printSource(new DOMSource(d)); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); // check base URI final String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val, MyWadlGeneratorConfig.MyWadlGenerator.CUSTOM_RESOURCES_BASE_URI); } @Path("emptyproduces") public static class EmptyProducesTestResource { @PUT @Produces({}) public Response put(final String str) { return Response.ok().build(); } @POST @Path("/sub") @Produces({}) public Response post(final String str) { return Response.ok().build(); } } @Test public void testEmptyProduces() throws Exception { final ResourceConfig rc = new ResourceConfig(EmptyProducesTestResource.class); rc.property(ServerProperties.WADL_FEATURE_DISABLE, false); final ApplicationHandler applicationHandler = new ApplicationHandler(rc); final ContainerResponse containerResponse = applicationHandler.apply(new ContainerRequest( URI.create("/"), URI.create("/application.wadl"), "GET", null, new MapPropertiesDelegate())).get(); assertEquals(200, containerResponse.getStatus()); ((ByteArrayInputStream) containerResponse.getEntity()).reset(); final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); bf.setValidating(false); if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) { bf.setXIncludeAware(false); } final DocumentBuilder b = bf.newDocumentBuilder(); final Document d = b.parse((InputStream) containerResponse.getEntity()); printSource(new DOMSource(d)); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); final NodeList responseElements = (NodeList) xp.evaluate( "/wadl:application/wadl:resources[@path!='application.wadl']//wadl:method/wadl:response", d, XPathConstants.NODESET); for (int i = 0; i < responseElements.getLength(); i++) { final Node item = responseElements.item(i); assertEquals(0, item.getChildNodes().getLength()); assertEquals(0, item.getAttributes().getLength()); } } } public static class Wadl1Test extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(RootResource.class); } @Path("root") public static class RootResource { @Path("loc") public Object getSub() { return new SubResource(); } @Path("switch") @POST public void switchMethod(@Context final WadlApplicationContext wadlApplicationContext) { wadlApplicationContext.setWadlGenerationEnabled(!wadlApplicationContext.isWadlGenerationEnabled()); } } public static class SubResource { @Path("loc") public Object getSub() { return new SubResource(); } @GET @Produces("text/plain") public String hello() { return "Hello World !"; } @GET @Path("sub") @Produces("text/plain") public String helloSub() { return "Hello World !"; } } @Test public void testRecursive() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { _testRecursiveWadl("root/loc"); } @Test public void mytest() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { final Response response = target().path("root/test").request().get(); System.out.println(response.readEntity(String.class)); } @Test public void testRecursive2() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { _testRecursiveWadl("root/loc/loc"); } private void _testRecursiveWadl(final String path) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { final Document d = extractWadlAsDocument(target(path).request(MediaTypes.WADL_TYPE).options()); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val, getBaseUri().toString()); // check only one resource with for 'root/loc' val = (String) xp.evaluate("count(//wadl:resource[@path='" + path + "'])", d, XPathConstants.STRING); assertEquals(val, "1"); } } public static class Wadl2Test extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(RootResource1.class, RootResource2.class); } @Path("root1") public static class RootResource1 { @Path("loc") public Wadl1Test.SubResource getSub() { return new Wadl1Test.SubResource(); } } @Path("root2") public static class RootResource2 { @Path("loc") public Wadl1Test.SubResource getSub() { return new Wadl1Test.SubResource(); } } @Test public void testRecursive2() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { final Document d = extractWadlAsDocument(target("/application.wadl") .queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request().get()); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val, getBaseUri().toString()); // check only one resource with for 'root/loc' val = (String) xp.evaluate("count(//wadl:resource[@path='loc'])", d, XPathConstants.STRING); assertEquals("4", val); // check for method with id of hello val = (String) xp.evaluate("count(//wadl:resource[@path='loc']/wadl:method[@id='hello'])", d, XPathConstants.STRING); assertEquals("2", val); } } public static class Wadl3Test extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(FormResource.class); } @Path("form") public static class FormResource { @POST @Consumes("application/x-www-form-urlencoded") public void post( @FormParam("a") final String a, @FormParam("b") final String b, @FormParam("c") final JaxbBean c, @FormParam("c") final FormDataContentDisposition cdc, final Form form) { } } @Test public void testFormParam() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { final Document d = extractWadlAsDocument(target("/application.wadl").request().get()); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); final String requestPath = "//wadl:resource[@path='form']/wadl:method[@name='POST']/wadl:request"; final String representationPath = requestPath + "/wadl:representation"; // check number of request params is zero int count = ((Double) xp.evaluate("count(" + requestPath + "/wadl:param)", d, XPathConstants.NUMBER)).intValue(); assertEquals(0, count); // check number of request representations is one count = ((Double) xp.evaluate("count(" + representationPath + ")", d, XPathConstants.NUMBER)).intValue(); assertEquals(1, count); // check number of request representation params is three count = ((Double) xp.evaluate("count(" + representationPath + "/wadl:param)", d, XPathConstants.NUMBER)).intValue(); assertEquals(3, count); // check the style of the request representation param is 'query' String val = (String) xp.evaluate(representationPath + "/wadl:param[@name='a']/@style", d, XPathConstants.STRING); assertEquals("query", val); val = (String) xp.evaluate(representationPath + "/wadl:param[@name='b']/@style", d, XPathConstants.STRING); assertEquals("query", val); } } public static class Wadl4Test extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(FieldParamResource.class); } @Path("fieldParam/{pp}") public static class FieldParamResource { @HeaderParam("hp") String hp; @MatrixParam("mp") String mp; @PathParam("pp") String pp; private String q; @QueryParam("q") public void setQ(final String q) { this.q = q; } // these should not be included in WADL @Context UriInfo uriInfo; @Named("fakeParam") String fakeParam; @GET @Produces("text/plain") public String get() { return pp; } } @Test public void testFieldParam() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { final String path = "fieldParam"; _testFieldAndSetterParam(target("/application.wadl").request().get(), path); } private static void _testFieldAndSetterParam(final Response response, final String path) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { final Document d = extractWadlAsDocument(response); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); final String resourcePath = String.format("//wadl:resource[@path='%s/{pp}']", path); final String methodPath = resourcePath + "/wadl:method[@name='GET']"; // check number of resource methods is one final int methodCount = ((Double) xp.evaluate("count(" + methodPath + ")", d, XPathConstants.NUMBER)).intValue(); assertThat("Unexpected number of methods on path '" + methodPath + "'", methodCount, equalTo(1)); final Map<String, String> paramStyles = new HashMap<>(); paramStyles.put("mp", "matrix"); paramStyles.put("pp", "template"); // check the number of resource params final String resourceParamsCountXPath = String.format("count(%s/wadl:param)", resourcePath); final int resourceParamsCount = ((Double) xp.evaluate(resourceParamsCountXPath, d, XPathConstants.NUMBER)).intValue(); assertThat("Number of resource parameters does not match.", resourceParamsCount, equalTo(2)); for (final Map.Entry<String, String> param : paramStyles.entrySet()) { final String pName = param.getKey(); final String pStyle = param.getValue(); final String paramXPath = String.format("%s/wadl:param[@name='%s']", resourcePath, pName); // check number of params is one final int pc = ((Double) xp.evaluate("count(" + paramXPath + ")", d, XPathConstants.NUMBER)).intValue(); assertThat("Number of " + pStyle + " parameters '" + pName + "' does not match.", pc, equalTo(1)); // check the style of the param final String style = (String) xp.evaluate(paramXPath + "/@style", d, XPathConstants.STRING); assertThat("Parameter '" + pName + "' style does not match.", pStyle, equalTo(style)); } paramStyles.clear(); paramStyles.put("hp", "header"); paramStyles.put("q", "query"); // check the number of request params final String requestParamsCountXPath = String.format("count(%s/wadl:request/wadl:param)", methodPath); final int requestParamsCount = ((Double) xp.evaluate(requestParamsCountXPath, d, XPathConstants.NUMBER)).intValue(); assertThat("Number of request parameters does not match.", requestParamsCount, equalTo(2)); for (final Map.Entry<String, String> param : paramStyles.entrySet()) { final String pName = param.getKey(); final String pStyle = param.getValue(); final String paramXPath = String.format("%s/wadl:request/wadl:param[@name='%s']", methodPath, pName); // check that the number of params is one final int pc = ((Double) xp.evaluate("count(" + paramXPath + ")", d, XPathConstants.NUMBER)).intValue(); assertThat("Number of " + pStyle + " parameters '" + pName + "' does not match.", pc, equalTo(1)); // check the style of the param final String style = (String) xp.evaluate(paramXPath + "/@style", d, XPathConstants.STRING); assertThat("Parameter '" + pName + "' style does not match.", pStyle, equalTo(style)); } } } /** * Tests OPTIONS method on a resource method annotated with @Path and containing a leading '/'. */ public static class Wadl6Test extends JerseyTest { @Path("wadl6test") public static class Resource { @GET @Path("foo1") public String foo1() { return "foo1"; } @GET @Path("/foo2") public String foo2() { return "foo2"; } @GET @Path("foo3/") public String foo3() { return "foo3"; } @GET @Path("/foo4/") public String foo4() { return "foo4"; } } @Override protected Application configure() { enable(TestProperties.LOG_TRAFFIC); enable(TestProperties.DUMP_ENTITY); return new ResourceConfig(Resource.class); } @Test public void testGetWithPathAndLeadingSlash() throws Exception { for (int i = 1; i < 5; i++) { final String[] paths = { "foo" + i, "/foo" + i, "foo" + i + '/', "/foo" + i + '/' }; for (final String path : paths) { final Response response = target("wadl6test") .path(path) .request("application/vnd.sun.wadl+xml") .options(); assertEquals(200, response.getStatus()); final String document = response.readEntity(String.class); // check that the resulting document contains a method element with id="fooX" assertTrue(document.replaceAll("\n", " ").matches(".*<method[^>]+id=\"foo" + i + "\"[^>]*>.*")); } } } } public static class Wadl8Test extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(ResourceA.class, ResourceB.class, ResourceSpecific.class); } @Path("{a}") public static class ResourceA { @GET public String getA() { return "a"; } } @Path("{b}") public static class ResourceB { @POST public String postB(final String str) { return "b"; } } @Path("resource") public static class ResourceSpecific { @GET @Path("{templateA}") public String getTemplateA() { return "template-a"; } @POST @Path("{templateB}") public String postTemplateB(final String str) { return "template-b"; } } @Test @Ignore("JERSEY-1670: WADL Options invoked on resources with same template returns only methods from one of them.") // TODO: fix public void testWadlForAmbiguousResourceTemplates() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException { final Response response = target().path("foo").request(MediaTypes.WADL_TYPE).options(); final Document d = extractWadlAsDocument(response); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); String result = (String) xp.evaluate("//wadl:resource/wadl:method[@name='GET']/@id", d, XPathConstants.STRING); assertEquals("getA", result); result = (String) xp.evaluate("//wadl:resource/wadl:method[@name='POST']/@id", d, XPathConstants.STRING); assertEquals("postB", result); } @Test @Ignore("JERSEY-1670: WADL Options invoked on resources with same template returns only methods from one of them.") // TODO: fix public void testWadlForAmbiguousChildResourceTemplates() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException { final Response response = target().path("resource/bar").request(MediaTypes.WADL_TYPE).options(); final Document d = extractWadlAsDocument(response); final XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02")); String result = (String) xp.evaluate("//wadl:resource/wadl:method[@name='GET']/@id", d, XPathConstants.STRING); assertEquals("getTemplateA", result); result = (String) xp.evaluate("//wadl:resource/wadl:method[@name='POST']/@id", d, XPathConstants.STRING); assertEquals("postTemplateB", result); } } /** * Tests usage of property {@link ServerProperties#METAINF_SERVICES_LOOKUP_DISABLE}. Wadl is registered automatically every * time and can be turned off only via {@link ServerProperties#WADL_FEATURE_DISABLE}. */ public static class Wadl9Test extends JerseyTest { @Path("wadl9test") public static class Resource { @GET public String foo() { return "foo"; } } // class Resource @Override protected Application configure() { final ResourceConfig resourceConfig = new ResourceConfig(Resource.class); resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true); return resourceConfig; } @Test public void testWadl() throws Exception { final Response response = target("/application.wadl").request().get(); assertThat(response.getStatus(), is(200)); assertThat(response.hasEntity(), is(true)); } } // class Wadl9Test /** * Tests whether boolean getters have been generated with "is" prefix. */ public static class Wadl10Test extends JerseyTest { @Path("wadl10test") public static class Resource { @GET public Boolean foo(@QueryParam("q") final Boolean q) { return q; } } // class Resource @Override protected Application configure() { return new ResourceConfig(Resource.class); } @Test public void testWadl() throws Exception { final Response response = target("/application.wadl").request().get(); assertThat(response.getStatus(), is(200)); assertThat(response.hasEntity(), is(true)); final Method method = (Method) response.readEntity(com.sun.research.ws.wadl.Application.class) // wadl .getResources().get(0).getResource().get(0) // resource .getMethodOrResource().get(0); // method final Param param = method.getRequest().getParam().get(0); // param // not interested in returned value, only whether we can compile. assertThat(param.isRequired(), notNullValue()); assertThat(param.isRepeating(), notNullValue()); } } // class Wadl10Test /** * Tests whether unknown annotation affects a WADL correctness. */ public static class Wadl11Test extends JerseyTest { @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface UnknownAnnotation { } @Path("annotated") @Consumes("application/json") @UnknownAnnotation public static class Annotated { @Path("subresource") @POST @Produces("application/json") @UnknownAnnotation public Entity myMethod1(@UnknownAnnotation final Entity entity) { return entity; } @Path("subresource") @POST @Produces("application/xml") public Entity myMethod2(@UnknownAnnotation final Entity entity) { return entity; } } @Path("not-annotated") @Consumes("application/json") public static class Plain { @Path("subresource") @POST @Produces("application/json") public Entity myMethod1(final Entity entity) { return entity; } @Path("subresource") @POST @Produces("application/xml") public Entity myMethod2(final Entity entity) { return entity; } } @Override protected Application configure() { return new ResourceConfig(Annotated.class, Plain.class); } @Test public void testWadlIsComplete() throws Exception { final Response response = target("/application.wadl").request().get(); assertThat(response.getStatus(), is(200)); assertThat(response.hasEntity(), is(true)); final com.sun.research.ws.wadl.Application application = response.readEntity(com.sun.research.ws.wadl.Application.class); // "annotated/subresource" final Resource resource = (Resource) application.getResources().get(0).getResource().get(0).getMethodOrResource().get(0); assertThatMethodContainsRR(resource, "myMethod1", 0); assertThatMethodContainsRR(resource, "myMethod2", 1); } private static void assertThatMethodContainsRR(final Resource resource, final String methodName, final int methodIndex) { final Method method = (Method) resource.getMethodOrResource().get(methodIndex); assertThat(method.getId(), equalTo(methodName)); final Request request = method.getRequest(); final List<com.sun.research.ws.wadl.Response> response = method.getResponse(); assertThat(request, notNullValue()); assertThat(response.isEmpty(), is(false)); } @Test public void testWadlIsSameForAnnotatedAndNot() throws Exception { final Response response = target("/application.wadl").request().get(); final Document document = extractWadlAsDocument(response); final XPath xp = XPathFactory.newInstance().newXPath(); final SimpleNamespaceResolver nsContext = new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"); xp.setNamespaceContext(nsContext); final Diff diff = XMLUnit.compareXML( nodeAsString( xp.evaluate("//wadl:resource[@path='annotated']/wadl:resource", document, XPathConstants.NODE)), nodeAsString( xp.evaluate("//wadl:resource[@path='not-annotated']/wadl:resource", document, XPathConstants.NODE)) ); XMLUnit.setXpathNamespaceContext( new SimpleNamespaceContext(ImmutableMap.of("wadl", "http://wadl.dev.java.net/2009/02"))); final ElementQualifier elementQualifier = new RecursiveElementNameAndTextQualifier(); diff.overrideElementQualifier(elementQualifier); XMLAssert.assertXMLEqual(diff, true); } } // class Wadl11Test @XmlRootElement(name = "jaxbBean") public class JaxbBean { public String value; public JaxbBean() { } public JaxbBean(String str) { value = str; } public boolean equals(Object o) { if (!(o instanceof JaxbBean)) { return false; } return ((JaxbBean) o).value.equals(value); } public String toString() { return "JAXBClass: " + value; } } }