Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2012 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.server.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.ws.rs.BeanParam;
import javax.ws.rs.CookieParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.inject.Singleton;
import org.glassfish.jersey.internal.inject.Providers;
import org.glassfish.jersey.message.MessageBodyWorkers;
import org.glassfish.jersey.message.internal.MediaTypes;
import org.glassfish.jersey.server.internal.LocalizationMessages;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.ServiceLocator;
/**
* Performs a basic check on abstract resources.
*
* Validity check populates a list of potential issues with the given resource.
* The issues are divided into two categories: fatal and non-fatal issues. The
* former type prevents the resource to be deployed and makes the whole web
* application deployment fail.
*
* To check a single resource class, one could use one of the {@link Resource}
* {@code builder(...)} methods to get a resource model.
*
* {@link ResourceModelValidator#validate(ResourceModelComponent)}
* method then populates the issue list, which could be then obtained by the
* {@link ResourceModelValidator#getIssueList()}. Unless you explicitly clear
* the list, subsequent calls to the validate method will add new items to the list,
* so that you can build the issue list for more than one resource. To clear the
* list, you may want to call {@link ResourceModelValidator#cleanIssueList()} method.
*
* @author Jakub Podlesak (jakub.podlesak at oracle.com)
* @author Marek Potociar (marek.potociar at oracle.com)
*/
public class BasicValidator extends ResourceModelValidator {
private static final Set> SCOPE_ANNOTATIONS = getScopeAnnotations();
private final ServiceLocator locator;
private static Set> getScopeAnnotations() {
Set> scopeAnnotations = new HashSet>();
scopeAnnotations.add(Singleton.class);
scopeAnnotations.add(PerLookup.class);
return scopeAnnotations;
}
private final MessageBodyWorkers workers;
/**
* Classes that have been checked already.
*/
protected final Set> checkedClasses = new HashSet>();
/**
* Construct a new basic validator with an empty issue list.
*
* @param locator HK2 service locator.
*/
@SuppressWarnings("UnusedDeclaration")
public BasicValidator(ServiceLocator locator) {
this(new LinkedList(), locator);
}
/**
* Construct a new basic validator.
*
* @param issueList validation issue list.
* @param locator HK2 service locator.
*/
public BasicValidator(List issueList, ServiceLocator locator) {
super(issueList);
this.workers = locator.getService(MessageBodyWorkers.class);
this.locator = locator;
}
@Override
public void visitResourceClass(Resource resource) {
// uri template of the resource, if present should not contain a null value
if (resource.isRootResource() && (null == resource.getPath())) {
// TODO: is it really a fatal issue?
addFatalIssue(resource, LocalizationMessages.RES_URI_PATH_INVALID(resource.getName(), resource.getPath()));
}
checkConsumesProducesAmbiguities(resource);
checkSRLAmbiguities(resource);
}
@Override
public void visitResourceHandlerConstructor(HandlerConstructor constructor) {
Class> resClass = constructor.getConstructor().getDeclaringClass();
boolean isSingleton = isSingleton(resClass);
int paramCount = 0;
for (Parameter p : constructor.getParameters()) {
validateParameter(getIssueList(), p, constructor.getConstructor(), constructor.getConstructor().toGenericString(),
Integer.toString(++paramCount),
isSingleton);
}
}
/**
* Check if the resource class is declared to be a singleton.
*
* @param resourceClass resource class.
* @return {@code true} if the resource class is a singleton, {@code false} otherwise.
*/
public static boolean isSingleton(Class> resourceClass) {
return resourceClass.isAnnotationPresent(Singleton.class)
|| (Providers.isProvider(resourceClass) && !resourceClass.isAnnotationPresent(PerLookup.class));
}
@Override
public void visitInvocable(Invocable invocable) {
// TODO: check invocable.
Class resClass = invocable.getHandler().getHandlerClass();
if (resClass != null && !checkedClasses.contains(resClass)) {
checkedClasses.add(resClass);
final boolean provider = Providers.isProvider(resClass);
int counter = 0;
for (Annotation annotation : resClass.getAnnotations()) {
if (SCOPE_ANNOTATIONS.contains(annotation.annotationType())) {
counter++;
}
}
if (counter == 0 && provider) {
addMinorIssue(resClass, LocalizationMessages.RESOURCE_IMPLEMENTS_PROVIDER(resClass,
Providers.getProviderContracts(resClass)));
} else if (counter > 1) {
addFatalIssue(resClass, LocalizationMessages.RESOURCE_MULTIPLE_SCOPE_ANNOTATIONS(resClass));
}
}
}
@Override
public void visitMethodHandler(MethodHandler methodHandler) {
// TODO: check method handler.
}
@Override
public void visitResourceMethod(ResourceMethod method) {
switch (method.getType()) {
case RESOURCE_METHOD:
visitJaxrsResourceMethod(method);
break;
case SUB_RESOURCE_METHOD:
visitSubResourceMethod(method);
break;
case SUB_RESOURCE_LOCATOR:
visitSubResourceLocator(method);
break;
}
}
private void visitJaxrsResourceMethod(ResourceMethod method) {
checkMethod(method);
}
private void checkMethod(ResourceMethod method) {
checkValueProviders(method);
final Invocable invocable = method.getInvocable();
checkParameters(method);
if ("GET".equals(method.getHttpMethod())) {
// ensure GET returns non-void value if not suspendable
if (void.class == invocable.getHandlingMethod().getReturnType() && !method.isSuspendDeclared()) {
addMinorIssue(method, LocalizationMessages.GET_RETURNS_VOID(invocable.getHandlingMethod()));
}
// ensure GET does not consume an entity parameter, if not inflector-based
if (invocable.requiresEntity() && !invocable.isInflector()) {
addMinorIssue(method, LocalizationMessages.GET_CONSUMES_ENTITY(invocable.getHandlingMethod()));
}
// ensure GET does not consume any @FormParam annotated parameter
for (Parameter p : invocable.getParameters()) {
if (p.isAnnotationPresent(FormParam.class)) {
addFatalIssue(method, LocalizationMessages.GET_CONSUMES_FORM_PARAM(invocable.getHandlingMethod()));
break;
}
}
}
// ensure there is not multiple HTTP method designators specified on the method
List httpMethodAnnotations = new LinkedList();
for (Annotation a : invocable.getHandlingMethod().getDeclaredAnnotations()) {
if (null != a.annotationType().getAnnotation(HttpMethod.class)) {
httpMethodAnnotations.add(a.toString());
} else if ((a.annotationType() == Path.class) && method.getType() == ResourceMethod.JaxrsType.RESOURCE_METHOD) {
addMinorIssue(method, LocalizationMessages.SUB_RES_METHOD_TREATED_AS_RES_METHOD(
invocable.getHandlingMethod(), ((Path) a).value()));
}
}
if (httpMethodAnnotations.size() > 1) {
addFatalIssue(method, LocalizationMessages.MULTIPLE_HTTP_METHOD_DESIGNATORS(
invocable.getHandlingMethod(), httpMethodAnnotations.toString()));
}
final Type responseType = invocable.getResponseType();
if (!isConcreteType(responseType)) {
addMinorIssue(invocable.getHandlingMethod(),
LocalizationMessages.TYPE_OF_METHOD_NOT_RESOLVABLE_TO_CONCRETE_TYPE(
responseType, invocable.getHandlingMethod().toGenericString()));
}
}
private void visitSubResourceMethod(ResourceMethod method) {
// check the same things that are being checked for resource methods
checkMethod(method);
// and make sure the Path is not null
if ((null == method.getPath()) || (null == method.getPath()) || (method.getPath().length() == 0)) {
addFatalIssue(method, LocalizationMessages.SUBRES_METHOD_URI_PATH_INVALID(
method.getInvocable().getHandlingMethod(), method.getPath()));
}
}
private void checkValueProviders(ResourceMethod method) {
final List> valueProviders = method.getInvocable().getValueProviders(locator);
if (valueProviders.contains(null)) {
int index = valueProviders.indexOf(null);
addFatalIssue(method, LocalizationMessages.ERROR_PARAMETER_MISSING_VALUE_PROVIDER(index,
method.getInvocable().getHandlingMethod()));
}
}
private void visitSubResourceLocator(ResourceMethod locator) {
checkParameters(locator);
checkValueProviders(locator);
final Invocable invocable = locator.getInvocable();
if (void.class == invocable.getRawResponseType()) {
addFatalIssue(locator, LocalizationMessages.SUBRES_LOC_RETURNS_VOID(invocable.getHandlingMethod()));
}
if ((null == locator.getPath()) || (null == locator.getPath()) || (locator.getPath().length() == 0)) {
addFatalIssue(locator,
LocalizationMessages.SUBRES_LOC_URI_PATH_INVALID(invocable.getHandlingMethod(), locator.getPath()));
}
}
private static final Set PARAM_ANNOTATION_SET = createParamAnnotationSet();
private static Set createParamAnnotationSet() {
Set set = new HashSet(6);
set.add(HeaderParam.class);
set.add(CookieParam.class);
set.add(MatrixParam.class);
set.add(QueryParam.class);
set.add(PathParam.class);
set.add(BeanParam.class);
return Collections.unmodifiableSet(set);
}
/**
* Validate a single parameter instance.
*
* @param issueList an existing list of issues that will be modified.
* @param parameter parameter to be validated.
* @param source parameter source; used for issue reporting.
* @param reportedSourceName source name; used for issue reporting.
* @param reportedParameterName parameter name; used for issue reporting.
* @param injectionsForbidden true if parameters cannot be injected by
* parameter annotations, eg. {@link HeaderParam @HeaderParam}.
*/
static void validateParameter(final List issueList,
final Parameter parameter,
final Object source,
final String reportedSourceName,
final String reportedParameterName, boolean injectionsForbidden) {
int counter = 0;
final Annotation[] annotations = parameter.getAnnotations();
for (Annotation a : annotations) {
if (PARAM_ANNOTATION_SET.contains(a.annotationType())) {
if (injectionsForbidden) {
issueList.add(new ResourceModelIssue(
source,
LocalizationMessages.SINGLETON_INJECTS_PARAMETER(reportedSourceName, reportedParameterName),
true));
break;
}
counter++;
if (counter > 1) {
issueList.add(new ResourceModelIssue(
source,
LocalizationMessages.AMBIGUOUS_PARAMETER(reportedSourceName, reportedParameterName),
false));
break;
}
}
}
final Type paramType = parameter.getType();
if (!isConcreteType(paramType)) {
issueList.add(new ResourceModelIssue(
source,
LocalizationMessages.PARAMETER_UNRESOLVABLE(reportedParameterName, paramType, reportedSourceName),
false));
}
}
private static boolean isConcreteType(Type t) {
if (t instanceof ParameterizedType) {
return isConcreteParameterizedType((ParameterizedType) t);
} else if (!(t instanceof Class)) {
// GenericArrayType, WildcardType, TypeVariable
return false;
}
return true;
}
private static boolean isConcreteParameterizedType(ParameterizedType pt) {
boolean isConcrete = true;
for (Type t : pt.getActualTypeArguments()) {
isConcrete &= isConcreteType(t);
}
return isConcrete;
}
private void checkParameters(ResourceMethod method) {
final Invocable invocable = method.getInvocable();
final Method handlingMethod = invocable.getHandlingMethod();
int paramCount = 0;
int nonAnnotetedParameters = 0;
for (Parameter p : invocable.getParameters()) {
validateParameter(getIssueList(), p, handlingMethod, handlingMethod.toGenericString(), Integer.toString(++paramCount),
false);
if (method.getType() == ResourceMethod.JaxrsType.SUB_RESOURCE_LOCATOR
&& Parameter.Source.ENTITY == p.getSource()) {
addFatalIssue(method, LocalizationMessages.SUBRES_LOC_HAS_ENTITY_PARAM(invocable.getHandlingMethod()));
} else if (p.getAnnotations().length == 0) {
nonAnnotetedParameters++;
if (nonAnnotetedParameters > 1) {
addFatalIssue(method, LocalizationMessages.AMBIGUOUS_NON_ANNOTATED_PARAMETER(invocable.getHandlingMethod(),
invocable.getHandlingMethod().getDeclaringClass()));
}
}
}
}
private void checkConsumesProducesAmbiguities(Resource resource) {
final List resourceMethods = resource.getResourceMethods();
if (resourceMethods.size() >= 2) {
for (ResourceMethod m1 : resourceMethods.subList(0, resourceMethods.size() - 1)) {
for (ResourceMethod m2 : resourceMethods.subList(resourceMethods.indexOf(m1) + 1, resourceMethods.size())) {
if (sameHttpMethod(m1, m2)) {
checkIntersectingMediaTypes(resource, m1.getHttpMethod(), m1, m2);
}
}
}
}
final List subResourceMethods = resource.getSubResourceMethods();
if (subResourceMethods.size() >= 2) {
for (ResourceMethod m1 : subResourceMethods.subList(0, subResourceMethods.size() - 1)) {
for (ResourceMethod m2 :
subResourceMethods.subList(subResourceMethods.indexOf(m1) + 1, subResourceMethods.size())) {
if (samePath(m1, m2) && sameHttpMethod(m1, m2)) {
checkIntersectingMediaTypes(resource, m1.getHttpMethod(), m1, m2);
}
}
}
}
}
private void checkSRLAmbiguities(Resource resource) {
final List subResourceLocators = resource.getSubResourceLocators();
if (subResourceLocators.size() >= 2) {
for (ResourceMethod m1 : subResourceLocators.subList(0, subResourceLocators.size() - 1)) {
for (ResourceMethod m2 :
subResourceLocators.subList(subResourceLocators.indexOf(m1) + 1, subResourceLocators.size())) {
if (samePath(m1, m2)) {
addFatalIssue(resource, LocalizationMessages.AMBIGUOUS_SRLS(
resource.getName(),
m1.getPath(),
m2.getPath()));
}
}
}
}
}
private void checkIntersectingMediaTypes(
Resource resource,
String httpMethod,
ResourceMethod m1,
ResourceMethod m2) {
final List inputTypes1 = getEffectiveInputTypes(m1);
final List inputTypes2 = getEffectiveInputTypes(m2);
final List outputTypes1 = getEffectiveOutputTypes(m1);
final List outputTypes2 = getEffectiveOutputTypes(m2);
boolean consumesFails;
boolean consumesOnlyIntersects = false;
if (m1.getConsumedTypes().isEmpty() || m2.getConsumedTypes().isEmpty()) {
consumesFails = inputTypes1.equals(inputTypes2);
if (!consumesFails) {
consumesOnlyIntersects = MediaTypes.intersect(inputTypes1, inputTypes2);
}
} else {
consumesFails = MediaTypes.intersect(inputTypes1, inputTypes2);
}
boolean producesFails;
boolean producesOnlyIntersects = false;
if (m1.getProducedTypes().isEmpty() || m2.getProducedTypes().isEmpty()) {
producesFails = outputTypes1.equals(outputTypes2);
if (!producesFails) {
producesOnlyIntersects = MediaTypes.intersect(outputTypes1, outputTypes2);
}
} else {
producesFails = MediaTypes.intersect(outputTypes1, outputTypes2);
}
if (consumesFails && producesFails) {
// fatal
final String rcName = resource.getName();
addFatalIssue(resource, LocalizationMessages.AMBIGUOUS_FATAL_RMS(rcName, httpMethod, m1.getInvocable()
.getHandlingMethod(), m2.getInvocable().getHandlingMethod()));
} else if ((producesFails && consumesOnlyIntersects) || (consumesFails && producesOnlyIntersects) ||
(consumesOnlyIntersects && producesOnlyIntersects)) {
// warning
final String rcName = resource.getName();
if (m1.getInvocable().requiresEntity()) {
addMinorIssue(resource, LocalizationMessages.AMBIGUOUS_RMS_IN(
rcName, httpMethod, m1.getInvocable().getHandlingMethod(), m2.getInvocable().getHandlingMethod()));
} else {
addMinorIssue(resource, LocalizationMessages.AMBIGUOUS_RMS_OUT(
rcName, httpMethod, m1.getInvocable().getHandlingMethod(), m2.getInvocable().getHandlingMethod()));
}
}
}
private static final List StarTypeList = Arrays.asList(new MediaType("*", "*"));
private List getEffectiveInputTypes(final ResourceMethod resourceMethod) {
if (!resourceMethod.getConsumedTypes().isEmpty()) {
return resourceMethod.getConsumedTypes();
}
List result = new LinkedList();
if (workers != null) {
for (Parameter p : resourceMethod.getInvocable().getParameters()) {
if (p.getSource() == Parameter.Source.ENTITY) {
result.addAll(workers.getMessageBodyReaderMediaTypes(
p.getRawType(), p.getType(), p.getDeclaredAnnotations()));
}
}
}
return result.isEmpty() ? StarTypeList : result;
}
private List getEffectiveOutputTypes(final ResourceMethod resourceMethod) {
if (!resourceMethod.getProducedTypes().isEmpty()) {
return resourceMethod.getProducedTypes();
}
List result = new LinkedList();
if (workers != null) {
final Invocable invocable = resourceMethod.getInvocable();
result.addAll(workers.getMessageBodyWriterMediaTypes(
invocable.getRawResponseType(),
invocable.getResponseType(),
invocable.getHandlingMethod().getDeclaredAnnotations()));
}
return result.isEmpty() ? StarTypeList : result;
}
private boolean sameHttpMethod(ResourceMethod m1, ResourceMethod m2) {
return m1.getHttpMethod().equals(m2.getHttpMethod());
}
private boolean samePath(Routed m1, Routed m2) {
return m1.getPathPattern().equals(m2.getPathPattern());
}
}