/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2010-2013 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 com.sun.jersey.impl.wadl; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.regex.Pattern; 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.core.Context; import javax.ws.rs.core.Response; import javax.xml.XMLConstants; 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.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 com.sun.jersey.api.JResponse; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.filter.LoggingFilter; import com.sun.jersey.api.core.DefaultResourceConfig; import com.sun.jersey.api.core.ResourceConfig; import com.sun.jersey.api.representation.Form; import com.sun.jersey.api.wadl.config.WadlGeneratorConfig; import com.sun.jersey.api.wadl.config.WadlGeneratorDescription; import com.sun.jersey.core.header.FormDataContentDisposition; import com.sun.jersey.core.header.MediaTypes; import com.sun.jersey.core.util.SaxHelper; import com.sun.jersey.impl.AbstractResourceTester; import com.sun.jersey.impl.entity.JAXBBean; import com.sun.jersey.server.wadl.WadlApplicationContext; import com.sun.jersey.server.wadl.WadlGenerator; import com.sun.jersey.server.wadl.WadlGeneratorImpl; 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 com.sun.research.ws.wadl.Resources; /** * Wadl test cases. * * @author mh124079 * @author Michal Gajdos (michal.gajdos at oracle.com) */ public class WadlResourceTest extends AbstractResourceTester { public WadlResourceTest(String testName) { super(testName); } @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(String bar, @MatrixParam("test") String test) { return bar; } @PUT @Path("{id}") @Consumes("application/xml") public void updateWidget(String bar, @PathParam("id")int id) { } @GET @Path("{id}") @Produces({"application/xml", "application/json"}) public String getWidget(@PathParam("id")int id) { return null; } @DELETE @Path("{id}") public void deleteWidget(@PathParam("id")int id) { } @Path("{id}/verbose") public ExtraResource getVerbose(@PathParam("id")int id) { return new ExtraResource(); } } public void testDisableWadl() { ResourceConfig rc = new DefaultResourceConfig(WidgetsResource.class, ExtraResource.class); rc.getFeatures().put(ResourceConfig.FEATURE_DISABLE_WADL, true); initiateWebApplication(rc); WebResource r = resource("/application.wadl", false); ClientResponse cr = r.get(ClientResponse.class); assertEquals(404, cr.getStatus()); } public void testEnableWadl() { ResourceConfig rc = new DefaultResourceConfig(WidgetsResource.class, ExtraResource.class); rc.getFeatures().put(ResourceConfig.FEATURE_DISABLE_WADL, false); initiateWebApplication(rc); WebResource r = resource("/application.wadl", false); ClientResponse cr = r.get(ClientResponse.class); assertEquals(200, cr.getStatus()); } /** * Test WADL generation. * * @throws Exception in case of unexpected test failure. */ public void testGetWadl() throws Exception { initiateWebApplication(WidgetsResource.class, ExtraResource.class); WebResource r = resource("/application.wadl"); File tmpFile = r.get(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("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,BASE_URI.toString()); // check total number of resources is 4 val = (String)xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING); assertEquals(val,"4"); // check only once resource with for {id} val = (String)xp.evaluate("count(//wadl:resource[@path='{id}'])", d, XPathConstants.STRING); assertEquals(val,"1"); // check only once resource with for {id}/verbose val = (String)xp.evaluate("count(//wadl:resource[@path='{id}/verbose'])", d, XPathConstants.STRING); assertEquals(val,"1"); // check only once resource with for widgets val = (String)xp.evaluate("count(//wadl:resource[@path='widgets'])", d, XPathConstants.STRING); assertEquals(val,"1"); // check 3 methods for {id} val = (String)xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method)", d, XPathConstants.STRING); assertEquals(val,"3"); // check 2 methods for widgets val = (String)xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method)", d, XPathConstants.STRING); assertEquals(val,"2"); // check 1 matrix param on resource val = (String)xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:param[@name='test'])", d, XPathConstants.STRING); assertEquals(val,"1"); // 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(val,"2"); // 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(val,"1"); // 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"); } public void testLastModifiedGET() { initiateWebApplication(WidgetsResource.class, ExtraResource.class); WebResource r = resource("/application.wadl"); ClientResponse cr = r.get(ClientResponse.class); assertTrue(cr.getHeaders().containsKey("Last-modified")); } public void testLastModifiedOPTIONS() { initiateWebApplication(WidgetsResource.class, ExtraResource.class); WebResource r = resource("/widgets/3/verbose"); ClientResponse cr = r.options(ClientResponse.class); assertTrue(cr.getHeaders().containsKey("Last-modified")); } public void testOptionsResourceWadl() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { initiateWebApplication(WidgetsResource.class, ExtraResource.class); WebResource r = resource("/widgets"); // test WidgetsResource File tmpFile = r.options(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("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,BASE_URI.toString()); // check total number of resources is 3 (no ExtraResource details included) val = (String)xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING); assertEquals(val,"3"); // check only once resource with for {id} val = (String)xp.evaluate("count(//wadl:resource[@path='{id}'])", d, XPathConstants.STRING); assertEquals(val,"1"); // check only once resource with for {id}/verbose val = (String)xp.evaluate("count(//wadl:resource[@path='{id}/verbose'])", d, XPathConstants.STRING); assertEquals(val,"1"); // check only once resource with for widgets val = (String)xp.evaluate("count(//wadl:resource[@path='widgets'])", d, XPathConstants.STRING); assertEquals(val,"1"); // check 3 methods for {id} val = (String)xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method)", d, XPathConstants.STRING); assertEquals(val,"3"); // check 2 methods for widgets val = (String)xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method)", d, XPathConstants.STRING); assertEquals(val,"2"); // 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(val,"2"); // 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(val,"1"); // test ExtraResource r = resource("/foo"); tmpFile = r.options(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,BASE_URI.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 foo val = (String)xp.evaluate("count(//wadl:resource[@path='foo'])", d, XPathConstants.STRING); assertEquals(val,"1"); // check 1 methods for foo val = (String)xp.evaluate("count(//wadl:resource[@path='foo']/wadl:method)", d, XPathConstants.STRING); assertEquals(val,"1"); } private 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"; } public void testOptionsLocatorWadl() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { initiateWebApplication(WidgetsResource.class, ExtraResource.class); WebResource r = resource("/widgets/3/verbose"); // test WidgetsResource File tmpFile = r.accept(MediaTypes.WADL).options(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("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,BASE_URI.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(val,"1"); } public void testOptionsSubResourceWadl() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { initiateWebApplication(WidgetsResource.class, ExtraResource.class); WebResource r = resource("/widgets/3"); // test WidgetsResource File tmpFile = r.accept(MediaTypes.WADL).options(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("wadl", "http://wadl.dev.java.net/2009/02")); String val = (String)xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val,BASE_URI.toString()); // check total number of resources is 1 val = (String)xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING); assertEquals(val,"1"); // check only one resource with for {id} val = (String)xp.evaluate("count(//wadl:resource[@path='widgets/3'])", d, XPathConstants.STRING); assertEquals(val,"1"); // check 3 methods val = (String)xp.evaluate("count(//wadl:resource[@path='widgets/3']/wadl:method)", d, XPathConstants.STRING); assertEquals(val,"3"); } @Path("root") public static class RootResource { @Path("loc") public Object getSub() { return new SubResource(); } @Path("switch") @POST public void switchMethod(@Context 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 !"; } } public void testRecursive() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { initiateWebApplication(RootResource.class); WebResource r = resource("/root/loc"); // test WidgetsResource File tmpFile = r.accept(MediaTypes.WADL).options(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("wadl", "http://wadl.dev.java.net/2009/02")); String val = (String)xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val,BASE_URI.toString()); // check only one resource with for 'root/loc' val = (String)xp.evaluate("count(//wadl:resource[@path='root/loc'])", d, XPathConstants.STRING); assertEquals(val,"1"); r = resource("/root/loc/loc"); // test WidgetsResource tmpFile = r.accept(MediaTypes.WADL).options(File.class); bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); bf.setValidating(false); if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) { bf.setXIncludeAware(false); } b = bf.newDocumentBuilder(); d = b.parse(tmpFile); printSource(new DOMSource(d)); xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("wadl", "http://wadl.dev.java.net/2009/02")); val = (String)xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val,BASE_URI.toString()); // check only one resource with for 'root/loc' val = (String)xp.evaluate("count(//wadl:resource[@path='root/loc/loc'])", d, XPathConstants.STRING); assertEquals(val,"1"); } @Path("root1") public static class RootResource1 { @Path("loc") public SubResource getSub() { return new SubResource(); } } @Path("root2") public static class RootResource2 { @Path("loc") public SubResource getSub() { return new SubResource(); } } public void testRecursive2() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { initiateWebApplication(RootResource1.class, RootResource2.class); WebResource r = resource("/application.wadl"); File tmpFile = r.get(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("wadl", "http://wadl.dev.java.net/2009/02")); String val = (String)xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING); assertEquals(val,BASE_URI.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); } @Path("form") public static class FormResource { @POST @Consumes( "application/x-www-form-urlencoded" ) public void post( @FormParam( "a" ) String a, @FormParam( "b" ) String b, @FormParam( "c" ) JAXBBean c, @FormParam( "c" ) FormDataContentDisposition cdc, Form form ) { } } public void testFormParam() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { initiateWebApplication(FormResource.class); WebResource r = resource("/application.wadl"); File tmpFile = r.get(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("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 ); } @Path("fieldParam/{pp}") public static class FieldParamResource { @HeaderParam("hp") String hp; @MatrixParam("mp") String mp; @PathParam("pp") String pp; @QueryParam("q") String q; @GET @Produces("text/plain" ) public String get() { return pp; } } public void testFieldParam() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { _testFieldAndSetterParam(FieldParamResource.class, "fieldParam"); } @Path("setterParam/{pp}") public static class SetterParamResource { @HeaderParam("hp") public void setHp(String hp) {}; @MatrixParam("mp") public void setMp(String mp) {}; @PathParam("pp") public void setPP(String pp) {}; @QueryParam("q") public void setQ(String q) {}; @GET @Produces("text/plain" ) public String get() { return "nonsense"; } } public void testSetterParam() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { _testFieldAndSetterParam(SetterParamResource.class, "setterParam"); } public void testEnableDisableRuntime() { initiateWebApplication(RootResource.class); WebResource r = resource("/", false); r.addFilter(new LoggingFilter()); ClientResponse response = r.path("application.wadl").get(ClientResponse.class); assertTrue(response.getStatus() == 200); response = r.path("root").options(ClientResponse.class); assertTrue(response.getStatus() == 200); r.path("root/switch").post(); response = r.path("application.wadl").get(ClientResponse.class); assertTrue(response.getStatus() == 404); response = r.path("root").options(ClientResponse.class); assertTrue(response.getStatus() == 204); r.path("root/switch").post(); response = r.path("application.wadl").get(ClientResponse.class); assertTrue(response.getStatus() == 200); response = r.path("root").options(ClientResponse.class); assertTrue(response.getStatus() == 200); } private void _testFieldAndSetterParam(Class resourceClass, String path) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { initiateWebApplication(resourceClass); WebResource r = resource("/application.wadl"); File tmpFile = r.get(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("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 int methodCount = ( (Double)xp.evaluate("count(" + methodPath + ")", d, XPathConstants.NUMBER) ).intValue(); assertEquals(1, methodCount ); Map<String, String> paramStyles = new HashMap<String, String>(); paramStyles.put("hp", "header"); paramStyles.put("mp", "matrix"); paramStyles.put("pp", "template"); paramStyles.put("q", "query"); for(Entry<String, String> param : paramStyles.entrySet()) { String pName = param.getKey(); String pStyle = param.getValue(); String paramXPath = String.format("%s/wadl:param[@name='%s']", resourcePath, pName); // check number of params is one int pc = ( (Double)xp.evaluate("count(" + paramXPath + ")", d, XPathConstants.NUMBER) ).intValue(); assertEquals(1, pc ); // check the style of the param String style = (String)xp.evaluate(paramXPath + "/@style", d, XPathConstants.STRING); assertEquals(pStyle, style ); } } private static void printSource(Source source) { try { System.out.println("---------------------"); Transformer trans = TransformerFactory.newInstance().newTransformer(); 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 (Exception e) { e.printStackTrace(); } } 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() { Resources resources = super.createResources(); resources.setBase(CUSTOM_RESOURCES_BASE_URI); return resources; } @Override public void setWadlGeneratorDelegate(WadlGenerator delegate) { // nothing } } } /** * Test overriding WADL's /application/resources/@base attribute. * * @throws Exception in case of unexpected test failure. */ public void testCustomWadlResourcesBaseUri() throws Exception { ResourceConfig rc = new DefaultResourceConfig(WidgetsResource.class, ExtraResource.class); rc.getProperties().put(ResourceConfig.PROPERTY_WADL_GENERATOR_CONFIG, "com.sun.jersey.impl.wadl.WadlResourceTest$MyWadlGeneratorConfig"); initiateWebApplication(rc); WebResource r = resource("/application.wadl"); File tmpFile = r.get(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("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, MyWadlGeneratorConfig.MyWadlGenerator.CUSTOM_RESOURCES_BASE_URI); } @Path("jresponse") public static class JResponseTestResource { @GET @Produces("text/plain") public JResponse<List<String>> getClichedMessage() { // Return some cliched textual content return JResponse.<List<String>>status(200).entity(new ArrayList() { { add("Hello world!"); } }).build(); } } public void testJresponse() throws Exception { ResourceConfig rc = new DefaultResourceConfig(JResponseTestResource.class); initiateWebApplication(rc); WebResource r = resource("/application.wadl"); assertTrue(r.get(String.class).length() > 0); } @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(); } } public void testEmptyProduces() throws Exception { ResourceConfig rc = new DefaultResourceConfig(EmptyProducesTestResource.class); initiateWebApplication(rc); WebResource r = resource("/application.wadl"); File tmpFile = r.get(File.class); 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)); XPath xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("wadl", "http://wadl.dev.java.net/2009/02")); final NodeList responseElements = (NodeList) xp.evaluate( "/wadl:application/wadl:resources//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()); } } @Path("wadl6test") public static class OptionsResource { @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"; } } /** * Tests OPTIONS method on a resource method annotated with @Path and containing a leading '/'. */ public void testGetWithPathAndLeadingSlash() throws Exception { ResourceConfig rc = new DefaultResourceConfig(OptionsResource.class); initiateWebApplication(rc); for (int i = 1; i < 5; i++) { final String[] paths = { "foo" + i, "/foo" + i, "foo" + i + '/', "/foo" + i + '/' }; for (final String path : paths) { final String document = resource("wadl6test"). path(path). accept("application/vnd.sun.wadl+xml"). options(String.class); // check that the resulting document contains a method element with id="fooX" assertTrue(Pattern. compile(".*<([\\w]+:)*method[^>]+id=\"foo" + i + "\"[^>]*>.*", Pattern.MULTILINE | Pattern.DOTALL). matcher(document). matches()); } } } @Path(value = "/wadl7test") public static class OptionSubResource { @GET @Path(value = "/sub") @Produces(value = "text/html") public Response headSub() { return Response.ok().build(); } } /** * On receipt of an OPTIONS request an implementation MUST: Generate an automatic response using the metadata provided by the * JAX-RS annotations on the matching class and its methods. */ public void testOptionSub() throws Exception { final ResourceConfig resourceConfig = new DefaultResourceConfig(OptionSubResource.class); initiateWebApplication(resourceConfig); final ClientResponse response = resource("wadl7test").path("sub").accept("text/html").options(ClientResponse.class); final Set<String> allow = response.getAllow(); assertEquals(204, response.getStatus()); assertEquals(new HashSet<String>(){{ add("GET"); add("OPTIONS"); add("HEAD"); }}, allow); } }