/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2011 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.server.impl.modelapi.validation;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.ws.rs.CookieParam;
import javax.ws.rs.DELETE;
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 org.junit.Test;
import static org.junit.Assert.*;
import com.sun.jersey.api.model.AbstractResource;
import com.sun.jersey.api.model.ResourceModelIssue;
import com.sun.jersey.server.impl.modelapi.annotation.IntrospectionModeller;
import com.sun.jersey.spi.resource.Singleton;
import javax.ws.rs.FormParam;
/**
*
* @author Jakub Podlesak (jakub.podlesak at oracle.com)
*/
public class BasicValidatorTest {
@Path("rootNonAmbigCtors")
public static class TestRootResourceNonAmbigCtors {
// TODO: hmmm, even if this is not ambiguous, it is strange; shall we warn, the 1st and the 2nd ctor won't be used?
public TestRootResourceNonAmbigCtors(@QueryParam("s") String s) {}
public TestRootResourceNonAmbigCtors(@QueryParam("n") int n) {}
public TestRootResourceNonAmbigCtors(@QueryParam("s") String s, @QueryParam("n") int n) {}
@GET
public String getIt() {
return "it";
}
}
@Test
public void testRootResourceNonAmbigConstructors() throws Exception {
System.out.println(
"---\nNo issue should be reported if more public ctors exists with the same number of params, " +
"but another just one is presented with more params at a root resource:");
AbstractResource ar = IntrospectionModeller.createResource(TestRootResourceNonAmbigCtors.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(validator.getIssueList().isEmpty());
}
@Singleton
@Path("rootSingleton/{p}")
public static class TestCantInjectFieldsForSingleton {
@MatrixParam("m")
String matrixParam;
@QueryParam("q")
String queryParam;
@PathParam("p")
String pParam;
@CookieParam("c")
String cParam;
@HeaderParam("h")
String hParam;
@GET
public String getIt() {
return "it";
}
}
// this should be sorted out at runtime rather than during validation
public void suspendedTestSingletonFieldsInjection() throws Exception {
System.out.println("---\nAn issue should be reported if injection is required for a singleton life-cycle:");
AbstractResource ar = IntrospectionModeller.createResource(TestCantInjectFieldsForSingleton.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
assertEquals(5, validator.getIssueList().size());
}
public static class TestNonPublicRM {
@GET
private String getIt() {
return "this";
}
}
@Test
public void testNonPublicRM() throws Exception {
System.out.println("---\nAn issue should be reported if a resource method is not public:");
AbstractResource ar = IntrospectionModeller.createResource(TestNonPublicRM.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
// TODO: there might still be an implicit viewable associated with it
// assertTrue(validator.getIssueList().get(0).isFatal());
}
public static class TestNonPublicRM1 {
@GET
private String getThis() {
return "this";
}
@PUT
public void putThis(String t) {
}
}
@Test
public void testNonPublicRM1() throws Exception {
System.out.println("---\nAn issue should be reported if a resource method is not public:");
AbstractResource ar = IntrospectionModeller.createResource(TestNonPublicRM1.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
}
public static class TestMoreThanOneEntity {
@PUT
public void put(String one, String two) {
}
}
// should be probably validated at runtime rather then at the validation phase
public void suspendedTestMoreThanOneEntity() throws Exception {
System.out.println("---\nAn issue should be reported if a resource method takes more than one entity params:");
AbstractResource ar = IntrospectionModeller.createResource(TestMoreThanOneEntity.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
}
public static class TestGetRMReturningVoid {
@GET
public void getMethod() {
}
}
@Test
public void testGetRMReturningVoid() throws Exception {
System.out.println("---\nAn issue should be reported if a get method returns void:");
AbstractResource ar = IntrospectionModeller.createResource(TestGetRMReturningVoid.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
assertTrue(!validator.getIssueList().get(0).isFatal());
}
public static class TestGetRMConsumingEntity {
@GET
public String getMethod(Object o) {
return "it";
}
}
@Test
public void testGetRMConsumingEntity() throws Exception {
System.out.println("---\nAn issue should be reported if a get method consumes an entity:");
AbstractResource ar = IntrospectionModeller.createResource(TestGetRMConsumingEntity.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
assertTrue(!validator.getIssueList().get(0).isFatal());
}
public static class TestGetRMConsumingFormParam {
@GET
public String getMethod(@FormParam("f") Object o, @FormParam("g") Object p) {
return "it";
}
}
@Test
public void testGetRMConsumingFormParam() throws Exception {
System.out.println("---\nAn issue should be reported if a get method consumes a form param:");
AbstractResource ar = IntrospectionModeller.createResource(TestGetRMConsumingFormParam.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(validator.getIssueList().size() == 1);
assertTrue(validator.getIssueList().get(0).isFatal());
}
public static class TestSRLReturningVoid {
@Path("srl")
public void srLocator() {
}
}
@Test
public void testSRLReturningVoid() throws Exception {
System.out.println("---\nAn issue should be reported if a sub-resource locator returns void:");
AbstractResource ar = IntrospectionModeller.createResource(TestSRLReturningVoid.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(validator.fatalIssuesFound());
}
public static class TestGetSRMReturningVoid {
@GET
@Path("srm")
public void getSRMethod() {
}
}
@Test
public void testGetSRMReturningVoid() throws Exception {
System.out.println("---\nAn issue should be reported if a get sub-resource method returns void:");
AbstractResource ar = IntrospectionModeller.createResource(TestGetSRMReturningVoid.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
assertTrue(!validator.getIssueList().get(0).isFatal());
}
public static class TestGetSRMConsumingEntity {
@Path("p")
@GET
public String getMethod(Object o) {
return "it";
}
}
@Test
public void testGetSRMConsumingEntity() throws Exception {
System.out.println("---\nAn issue should be reported if a get method consumes an entity:");
AbstractResource ar = IntrospectionModeller.createResource(TestGetSRMConsumingEntity.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
assertTrue(!validator.getIssueList().get(0).isFatal());
}
public static class TestGetSRMConsumingFormParam {
@GET @Path("p")
public String getMethod(@FormParam("f") Object o) {
return "it";
}
}
@Test
public void testGetSRMConsumingFormParam() throws Exception {
System.out.println("---\nAn issue should be reported if a get method consumes a form param:");
AbstractResource ar = IntrospectionModeller.createResource(TestGetSRMConsumingFormParam.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.getIssueList().isEmpty());
assertTrue(validator.getIssueList().get(0).isFatal());
}
@Path("rootMultipleHttpMethodDesignatorsRM")
public static class TestMultipleHttpMethodDesignatorsRM {
@GET
@PUT
public String getPutIt() {
return "it";
}
}
@Test
public void testMultipleHttpMethodDesignatorsRM() throws Exception {
System.out.println("---\nAn issue should be reported if more than one HTTP method designator exist on a resource method:");
AbstractResource ar = IntrospectionModeller.createResource(TestMultipleHttpMethodDesignatorsRM.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(validator.fatalIssuesFound());
}
@Path("rootMultipleHttpMethodDesignatorsSRM")
public static class TestMultipleHttpMethodDesignatorsSRM {
@Path("srm")
@POST
@PUT
public String postPutIt() {
return "it";
}
}
@Test
public void testMultipleHttpMethodDesignatorsSRM() throws Exception {
System.out.println("---\nAn issue should be reported if more than one HTTP method designator exist on a sub-resource method:");
AbstractResource ar = IntrospectionModeller.createResource(TestMultipleHttpMethodDesignatorsSRM.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(validator.fatalIssuesFound());
}
@Path("rootEntityParamOnSRL")
public static class TestEntityParamOnSRL {
@Path("srl")
public String locator(String s) {
return "it";
}
}
@Test
public void testEntityParamOnSRL() throws Exception {
System.out.println("---\nAn issue should be reported if an entity parameter exists on a sub-resource locator:");
AbstractResource ar = IntrospectionModeller.createResource(TestEntityParamOnSRL.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(validator.fatalIssuesFound());
}
@Path(value = "/DeleteTest")
public static class TestNonConflictingHttpMethodDelete {
static String html_content =
"<html>" + "<head><title>Delete text/html</title></head>" +
"<body>Delete text/html</body></html>";
@DELETE
@Produces(value = "text/plain")
public String getPlain() {
return "Delete text/plain";
}
@DELETE
@Produces(value = "text/html")
public String getHtml() {
return html_content;
}
@DELETE
@Path(value = "/sub")
@Produces(value = "text/html")
public String getSub() {
return html_content;
}
}
@Test
public void testNonConflictingHttpMethodDelete() throws Exception {
System.out.println("---\nNo issue should be reported if produced mime types differ");
AbstractResource ar = IntrospectionModeller.createResource(TestNonConflictingHttpMethodDelete.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(validator.getIssueList().isEmpty());
}
@Path(value = "/AmbigParamTest")
public static class TestAmbiguousParams {
@QueryParam("q")
@HeaderParam("q")
private int a;
@QueryParam("b")
@HeaderParam("b")
@MatrixParam("q")
public void setB(String b) {
}
@GET @Path("a")
public String get(@PathParam("a") @QueryParam("a") String a) {
return "hi";
}
@GET @Path("b")
public String getSub(@PathParam("a") @QueryParam("b") @MatrixParam("c") String a, @MatrixParam("m") @QueryParam("m") int i) {
return "hi";
}
@Path("c")
public Object getSubLoc(@MatrixParam("m") @CookieParam("c") String a) {
return null;
}
}
@Test
public void testAmbiguousParams() throws Exception {
System.out.println("---\nA warning should be reported if ambiguous source of a parameter is seen");
AbstractResource ar = IntrospectionModeller.createResource(TestAmbiguousParams.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.fatalIssuesFound());
assertEquals(6, validator.getIssueList().size());
}
@Path(value = "/EmptyPathSegmentTest")
public static class TestEmptyPathSegment {
@GET @Path("/")
public String get() {
return "hi";
}
}
@Test
public void testEmptyPathSegment() throws Exception {
System.out.println("---\nA warning should be reported if @Path with \"/\" or empty string value is seen");
AbstractResource ar = IntrospectionModeller.createResource(TestEmptyPathSegment.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.fatalIssuesFound());
assertEquals(1, validator.getIssueList().size());
}
public static class TypeVariableResource<T, V> {
@QueryParam("v") V fieldV;
V methodV;
@QueryParam("v")
public void set(V methodV) {
this.methodV = methodV;
}
@GET
public String get(@QueryParam("v") V getV) {
return getV.toString() + fieldV.toString() + methodV.toString();
}
@POST
public T post(T t) {
return t;
}
@Path("sub")
@POST
public T postSub(T t) {
return t;
}
}
@Test
public void testTypeVariableResource() throws Exception {
System.out.println("---\n");
AbstractResource ar = IntrospectionModeller.createResource(TypeVariableResource.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.fatalIssuesFound());
assertEquals(7, validator.getIssueList().size());
}
public static class ParameterizedTypeResource<T, V> {
@QueryParam("v") Collection<V> fieldV;
List<List<V>> methodV;
@QueryParam("v")
public void set(List<List<V>> methodV) {
this.methodV = methodV;
}
@GET
public String get(@QueryParam("v") Map<String, List<V>> getV) {
return "";
}
@POST
public Collection<T> post(Collection<T> t) {
return t;
}
@Path("sub")
@POST
public Collection<T> postSub(Collection<T> t) {
return t;
}
}
public static class ConcreteParameterizedTypeResource extends ParameterizedTypeResource<String, String> {
}
@Test
public void testParameterizedTypeResource() throws Exception {
System.out.println("---\n");
AbstractResource ar = IntrospectionModeller.createResource(ConcreteParameterizedTypeResource.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.fatalIssuesFound());
assertEquals(0, validator.getIssueList().size());
}
public static class GenericArrayResource<T, V> {
@QueryParam("v") V[] fieldV;
V[] methodV;
@QueryParam("v")
public void set(V[] methodV) {
this.methodV = methodV;
}
@GET
public String get(@QueryParam("v") V[] getV) {
return "";
}
@POST
public T[] post(T[] t) {
return t;
}
@Path("sub")
@POST
public T[] postSub(T[] t) {
return t;
}
}
public static class ConcreteGenericArrayResource extends GenericArrayResource<String, String> {
}
@Test
public void testGenericArrayResource() throws Exception {
System.out.println("---\n");
AbstractResource ar = IntrospectionModeller.createResource(ConcreteGenericArrayResource.class);
BasicValidator validator = new BasicValidator();
validator.validate(ar);
printIssueList(validator);
assertTrue(!validator.fatalIssuesFound());
assertEquals(0, validator.getIssueList().size());
}
// TODO: test multiple root resources with the same uriTempl (in WebApplicationImpl.processRootResources ?)
private static void printIssueList(BasicValidator validator) {
for (ResourceModelIssue issue : validator.getIssueList()) {
System.out.println((issue.isFatal() ? "ERROR: " : "WARNING: ") + issue.getMessage());
}
}
}