WadlBeanParamTest.java
/*
* Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
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 java.util.Collections;
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.xmlunit.builder.DiffBuilder;
import org.xmlunit.diff.ComparisonResult;
import org.xmlunit.diff.ComparisonType;
import org.xmlunit.diff.DefaultNodeMatcher;
import org.xmlunit.diff.Diff;
import org.xmlunit.diff.ElementSelectors;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* 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
*/
public class WadlBeanParamTest extends JerseyTest {
@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 = DiffBuilder.compare(
nodeAsString(
xp.evaluate("//wadl:resource[@path='wadlBeanParamReference']/wadl:resource", d,
XPathConstants.NODE)))
.withTest(
nodeAsString(
xp.evaluate("//wadl:resource[@path='" + resource + "']/wadl:resource", d,
XPathConstants.NODE))
).withNamespaceContext(Collections.singletonMap("wadl", "http://wadl.dev.java.net/2009/02"))
/**
* For nodes, the comparison is based on matching {@code name} attributes while ignoring
* their order. For any other nodes, strict comparison (including ordering) is made.
* **/
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndAttributes("name")))
.withDifferenceEvaluator(((comparison, outcome) -> {
if (outcome == ComparisonResult.DIFFERENT && comparison.getType() == ComparisonType.CHILD_NODELIST_SEQUENCE) {
return ComparisonResult.EQUAL;
}
return outcome;
}))
.build();
Assertions.assertFalse(diff.hasDifferences());
}
@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;
}
}
}