/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2010 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 * https://glassfish.dev.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. */ /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.sun.jersey.impl.wadl; import com.sun.jersey.api.client.ClientResponse; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.Properties; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; 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.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; 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 org.w3c.dom.Document; import org.xml.sax.SAXException; import com.sun.jersey.api.client.WebResource; 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.core.header.FormDataContentDisposition; import com.sun.jersey.core.header.MediaTypes; import com.sun.jersey.impl.AbstractResourceTester; import com.sun.jersey.impl.entity.JAXBBean; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.ws.rs.HeaderParam; import javax.ws.rs.MatrixParam; import javax.ws.rs.QueryParam; /** * * @author mh124079 */ 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) { 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 */ public void testGetWadl() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { 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); 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://research.sun.com/wadl/2006/10")); // 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 type of {id} is int val = (String)xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']/@type", d, XPathConstants.STRING); assertEquals(val,"xs: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 val = (String)xp.evaluate("//wadl:resource[@path='{id}/verbose']/wadl:param[@name='id']/@type", d, XPathConstants.STRING); assertEquals(val,"xs:int"); } 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); 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://research.sun.com/wadl/2006/10")); // 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 val = (String)xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']/@type", d, XPathConstants.STRING); assertEquals(val,"xs: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"); } 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); 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://research.sun.com/wadl/2006/10")); // 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); 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://research.sun.com/wadl/2006/10")); 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(); } } 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); 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://research.sun.com/wadl/2006/10")); 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); bf.setXIncludeAware(false); b = bf.newDocumentBuilder(); d = b.parse(tmpFile); printSource(new DOMSource(d)); xp = XPathFactory.newInstance().newXPath(); xp.setNamespaceContext(new NSResolver("wadl", "http://research.sun.com/wadl/2006/10")); 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); 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://research.sun.com/wadl/2006/10")); 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); 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://research.sun.com/wadl/2006/10")); 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"); } 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); 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://research.sun.com/wadl/2006/10")); 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 class NSResolver implements NamespaceContext { private String prefix; private String nsURI; public NSResolver(String prefix, String nsURI) { this.prefix = prefix; this.nsURI = nsURI; } @Override public String getNamespaceURI(String prefix) { if (prefix.equals(this.prefix)) return this.nsURI; else return XMLConstants.NULL_NS_URI; } @Override public String getPrefix(String namespaceURI) { if (namespaceURI.equals(this.nsURI)) return this.prefix; else return null; } @Override public Iterator getPrefixes(String namespaceURI) { return null; } } 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(); } } }