/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2015-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.StringWriter;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.BeanParam;
import javax.ws.rs.CookieParam;
import javax.ws.rs.Encoded;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
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.XPathFactory;
import org.glassfish.jersey.internal.util.SimpleNamespaceResolver;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.ElementNameAndTextQualifier;
import org.custommonkey.xmlunit.SimpleNamespaceContext;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.google.common.collect.ImmutableMap;
/**
* Tests whether WADL for a {@link BeanParam} annotated resource method parameter is generated properly.
* <p/>
* The tests of this class perform a comparison of
* <pre><ul>
* <li>a WADL of a reference resource that has {@code *Param} annotated class fields or resource method parameters</li>
* <li>with a resource configured with {@link BeanParam} annotated parameters where some of the reference resource parameters
* are aggregated in the class that is used as a bean param</li>
* </ul></pre>
*
* @author Stepan Vavra (stepan.vavra at oracle.com)
*/
public class WadlBeanParamTest extends JerseyTest {
private final ElementNameAndTextQualifier elementQualifier = new ElementNameAndTextQualifier() {
/**
* For {@code <param ??? />} nodes, the comparison is based on matching {@code name} attributes while ignoring
* their order. For any other nodes, strict comparison (including ordering) is made.
*
* @param control The reference element to compare the {@code test} with.
* @param test The test element to compare against {@code control}.
* @return Whether given nodes qualify for comparison.
*/
@Override
public boolean qualifyForComparison(final Element control, final Element test) {
if (test != null && !"param".equals(test.getNodeName())) {
return super.qualifyForComparison(control, test);
}
if (!(control != null && test != null
&& equalsNamespace(control, test)
&& getNonNamespacedNodeName(control).equals(getNonNamespacedNodeName(test)))) {
return false;
}
if (control.hasAttribute("name") && test.hasAttribute("name")) {
if (control.getAttribute("name").equals(test.getAttribute("name"))) {
return true;
}
}
return false;
}
};
@Override
protected Application configure() {
return new ResourceConfig(ReferenceResourceBeanParam.class, TestResourceBeanParam.class,
TestResourceConstructorInitializedBeanParam.class, TestResourceFieldBeanParam.class);
}
private 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();
}
/**
* Tests whether class with {@code *Param} annotated fields if used as a {@code BeanParam} annotated resource method parameter
* results in a correctly generated WADL.
*
* @throws Exception In case of any problem.
*/
@Test
public void testBeanParamFullBean() throws Exception {
testBeanParamConstructorInitializedBean("wadlBeanParamTest");
}
/**
* Tests whether class with {@code *Param} annotated constructor parameters if used as a {@code BeanParam} annotated resource
* method parameter results in a correctly generated WADL.
*
* @throws Exception In case of any problem.
*/
@Test
public void testBeanParamConstructorInitializedBean() throws Exception {
testBeanParamConstructorInitializedBean("wadlBeanParamConstructorInitializedTest");
}
/**
* Tests whether class with {@code *Param} annotated constructor parameters if used as a {@code BeanParam} annotated resource
* class field parameter results in a correctly generated WADL.
*
* @throws Exception In case of any problem.
*/
@Test
public void testBeanParamFieldBean() throws Exception {
testBeanParamConstructorInitializedBean("wadlBeanParamFieldTest");
}
private void testBeanParamConstructorInitializedBean(String resource) throws Exception {
final Response response = target("/application.wadl").request().get();
final Document d = WadlResourceTest.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='wadlBeanParamReference']/wadl:resource", d,
XPathConstants.NODE)),
nodeAsString(
xp.evaluate("//wadl:resource[@path='" + resource + "']/wadl:resource", d,
XPathConstants.NODE))
);
XMLUnit.setXpathNamespaceContext(
new SimpleNamespaceContext(ImmutableMap.of("wadl", "http://wadl.dev.java.net/2009/02")));
diff.overrideElementQualifier(elementQualifier);
XMLAssert.assertXMLEqual(diff, true);
}
@Path("wadlBeanParamReference")
private static class ReferenceResourceBeanParam {
@QueryParam("classFieldQueryParam")
private String classFieldQueryParam;
//////////////////////////////////////////////////////////////////
// following fields make this class compatible with 'FullBean', //
// 'ConstructorInitializedBean' and 'SmallBean' //
@HeaderParam("header")
private String headerParam;
@MatrixParam("matrix")
private String matrixParam;
@Encoded
@QueryParam("query")
private String queryParam;
@CookieParam("cookie")
private String cookie;
@FormParam("form")
private String formParam;
@POST
@Path("singleBean/{path}")
public String postBeanParam(/* pathParam is also extracted from FullBean */
@PathParam("path") String pathParam,
@QueryParam("methodParam") int methodParam,
@HeaderParam("header") String duplicateHeaderParam) {
return "";
}
}
@Path("wadlBeanParamTest")
private static class TestResourceBeanParam {
@QueryParam("classFieldQueryParam")
private String classFieldQueryParam;
@POST
@Path("singleBean/{path}")
public String postBeanParam(@BeanParam WadlFullBean bean,
@BeanParam WadlSmallBean wadlSmallBean,
@QueryParam("methodParam") int methodParam) {
return "";
}
}
@Path("wadlBeanParamConstructorInitializedTest")
private static class TestResourceConstructorInitializedBeanParam {
@QueryParam("classFieldQueryParam")
private String classFieldQueryParam;
@POST
@Path("singleBean/{path}")
public String postBeanParam(@BeanParam WadlConstructorInitializedBean bean,
@BeanParam WadlSmallBean wadlSmallBean,
@QueryParam("methodParam") int methodParam) {
return "";
}
}
@Path("wadlBeanParamFieldTest")
private static class TestResourceFieldBeanParam {
@QueryParam("classFieldQueryParam")
private String classFieldQueryParam;
@BeanParam
private WadlConstructorInitializedBean bean;
@BeanParam
private WadlSmallBean wadlSmallBean;
@POST
@Path("singleBean/{path}")
public String postBeanParam(@QueryParam("methodParam") int methodParam) {
return "";
}
}
/**
* The purpose of this unknown annotation is to verify that a usage of an unknown annotation in a {@link BeanParam} annotated
* class does not cause a failure during WADL generation.
*/
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface WadlUnknownAnnotation {
String value();
}
public static class WadlSmallBean {
@HeaderParam("header")
private String headerParam;
@PathParam("path")
private String pathParam;
public WadlSmallBean(WadlFullBean bean) {
headerParam = bean.getHeaderParam();
pathParam = bean.getPathParam();
}
}
public static class WadlEncodedBean {
@MatrixParam("matrix")
private String matrixParam;
@Encoded
@QueryParam("query")
private String queryParam;
public WadlEncodedBean(String matrixParam, String queryParam) {
this.matrixParam = matrixParam;
this.queryParam = queryParam;
}
}
public static class WadlFullBean {
@HeaderParam("header")
private String headerParam;
@PathParam("path")
private String pathParam;
@MatrixParam("matrix")
private String matrixParam;
@QueryParam("query")
private String queryParam;
@CookieParam("cookie")
private String cookie;
@FormParam("form")
private String formParam;
@WadlUnknownAnnotation("unknown")
private String unknownAnnotationParam;
@Context
private Request request;
private boolean overrideRequestNull;
public String getCookie() {
return cookie;
}
public void setCookie(String cookie) {
this.cookie = cookie;
}
public String getFormParam() {
return formParam;
}
public void setFormParam(String formParam) {
this.formParam = formParam;
}
public String getHeaderParam() {
return headerParam;
}
public void setHeaderParam(String headerParam) {
this.headerParam = headerParam;
}
public String getMatrixParam() {
return matrixParam;
}
public void setMatrixParam(String matrixParam) {
this.matrixParam = matrixParam;
}
public String getPathParam() {
return pathParam;
}
public void setPathParam(String pathParam) {
this.pathParam = pathParam;
}
public String getQueryParam() {
return queryParam;
}
public void setQueryParam(String queryParam) {
this.queryParam = queryParam;
}
public Request getRequest() {
return request;
}
public void setRequest(Request request) {
this.request = request;
}
public boolean isOverrideRequestNull() {
return overrideRequestNull;
}
public void setOverrideRequestNull(boolean overrideRequestNull) {
this.overrideRequestNull = overrideRequestNull;
}
public String getUnknownAnnotationParam() {
return unknownAnnotationParam;
}
public void setUnknownAnnotationParam(String unknownAnnotationParam) {
this.unknownAnnotationParam = unknownAnnotationParam;
}
}
public static class WadlConstructorInitializedBean {
private String headerParam;
private String pathParam;
private String matrixParam;
private String queryParam;
private String cookie;
private String formParam;
private String unknownAnnotationParam;
private Request request;
public WadlConstructorInitializedBean(@CookieParam("cookie") String cookie,
@FormParam("form") String formParam,
@HeaderParam("header") String headerParam,
@MatrixParam("matrix") String matrixParam,
@QueryParam("query") String queryParam,
@PathParam("path") String pathParam,
@WadlUnknownAnnotation("unknown") String unknownAnnotationParam,
@Context Request request) {
this.cookie = cookie;
this.formParam = formParam;
this.headerParam = headerParam;
this.matrixParam = matrixParam;
this.queryParam = queryParam;
this.pathParam = pathParam;
this.unknownAnnotationParam = unknownAnnotationParam;
this.request = request;
}
private boolean overrideRequestNull;
public String getCookie() {
return cookie;
}
public void setCookie(String cookie) {
this.cookie = cookie;
}
public String getFormParam() {
return formParam;
}
public void setFormParam(String formParam) {
this.formParam = formParam;
}
public String getHeaderParam() {
return headerParam;
}
public void setHeaderParam(String headerParam) {
this.headerParam = headerParam;
}
public String getMatrixParam() {
return matrixParam;
}
public void setMatrixParam(String matrixParam) {
this.matrixParam = matrixParam;
}
public String getPathParam() {
return pathParam;
}
public void setPathParam(String pathParam) {
this.pathParam = pathParam;
}
public String getQueryParam() {
return queryParam;
}
public void setQueryParam(String queryParam) {
this.queryParam = queryParam;
}
public Request getRequest() {
return request;
}
public void setRequest(Request request) {
this.request = request;
}
public boolean isOverrideRequestNull() {
return overrideRequestNull;
}
public void setOverrideRequestNull(boolean overrideRequestNull) {
this.overrideRequestNull = overrideRequestNull;
}
public String getUnknownAnnotationParam() {
return unknownAnnotationParam;
}
public void setUnknownAnnotationParam(String unknownAnnotationParam) {
this.unknownAnnotationParam = unknownAnnotationParam;
}
}
}