All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jersey.server.model.ResourceMethod Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.8
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2014 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.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.ws.rs.NameBinding;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MediaType;

import org.glassfish.jersey.message.internal.MediaTypes;
import org.glassfish.jersey.model.NameBound;
import org.glassfish.jersey.process.Inflector;
import org.glassfish.jersey.uri.PathPattern;

import jersey.repackaged.com.google.common.base.Function;
import jersey.repackaged.com.google.common.collect.Collections2;
import jersey.repackaged.com.google.common.collect.Lists;
import jersey.repackaged.com.google.common.collect.Sets;

/**
 * Model of a method available on a resource. Covers resource method, sub-resource
 * method and sub-resource locator.
 *
 * @author Marek Potociar (marek.potociar at oracle.com)
 */
public final class ResourceMethod implements ResourceModelComponent, Producing, Consuming, Suspendable, NameBound {

    /**
     * Resource method classification based on the recognized JAX-RS
     * resource method types.
     */
    public static enum JaxrsType {
        /**
         * JAX-RS resource method.
         * 

* Does not have a path template assigned. Is assigned to a particular HTTP method. */ RESOURCE_METHOD { @Override PathPattern createPatternFor(String pathTemplate) { // template is ignored. return PathPattern.END_OF_PATH_PATTERN; } }, /** * JAX-RS sub-resource method. *

* Has a sub-path template assigned and is assigned to a particular HTTP method. */ SUB_RESOURCE_METHOD { @Override PathPattern createPatternFor(String pathTemplate) { return new PathPattern(pathTemplate, PathPattern.RightHandPath.capturingZeroSegments); } }, /** * JAX-RS sub-resource locator. *

* Has a sub-path template assigned but is not assigned to any particular HTTP method. * Instead it produces a sub-resource instance that should be further * used in the request URI matching. */ SUB_RESOURCE_LOCATOR { @Override PathPattern createPatternFor(String pathTemplate) { return new PathPattern(pathTemplate, PathPattern.RightHandPath.capturingZeroOrMoreSegments); } }; /** * Create a proper matching path pattern from the provided template for * the selected method type. * * @param pathTemplate method path template. * @return method matching path pattern. */ /* package */ abstract PathPattern createPatternFor(String pathTemplate); private static JaxrsType classify(String httpMethod) { if (httpMethod != null && !httpMethod.isEmpty()) { return RESOURCE_METHOD; } else { return SUB_RESOURCE_LOCATOR; } } } /** * Resource method model builder. */ public static final class Builder { private final Resource.Builder parent; // HttpMethod private String httpMethod; // Consuming & Producing private final Set consumedTypes; private final Set producedTypes; // Suspendable private boolean managedAsync; private boolean suspended; private long suspendTimeout; private TimeUnit suspendTimeoutUnit; // Invocable private Class handlerClass; private Object handlerInstance; private Method definitionMethod; private Method handlingMethod; private boolean encodedParams; private Type routingResponseType; // NameBound private final Collection> nameBindings; private boolean extended; /** * Create a resource method builder. *

* The supplied parent resource model builder will be called to register * the newly created resource method instance as part of the {@link #build()} * method invocation. *

*

* Note that the {@link #build()} method does not have to be invoked manually * as the registration will happen automatically as part of the * {@link org.glassfish.jersey.server.model.Resource.Builder#build()} method * invocation. *

* * @param parent parent resource model builder. */ /* package */ Builder(final Resource.Builder parent) { this.parent = parent; this.httpMethod = null; this.consumedTypes = Sets.newLinkedHashSet(); this.producedTypes = Sets.newLinkedHashSet(); this.suspended = false; this.suspendTimeout = AsyncResponse.NO_TIMEOUT; this.suspendTimeoutUnit = TimeUnit.MILLISECONDS; this.encodedParams = false; this.nameBindings = Sets.newLinkedHashSet(); } /* package */ Builder(final Resource.Builder parent, ResourceMethod originalMethod) { this.parent = parent; this.consumedTypes = Sets.newLinkedHashSet(originalMethod.getConsumedTypes()); this.producedTypes = Sets.newLinkedHashSet(originalMethod.getProducedTypes()); this.suspended = originalMethod.isSuspendDeclared(); this.suspendTimeout = originalMethod.getSuspendTimeout(); this.suspendTimeoutUnit = originalMethod.getSuspendTimeoutUnit(); this.nameBindings = originalMethod.getNameBindings(); this.httpMethod = originalMethod.getHttpMethod(); this.managedAsync = originalMethod.isManagedAsyncDeclared(); Invocable invocable = originalMethod.getInvocable(); this.handlingMethod = invocable.getHandlingMethod(); this.encodedParams = false; this.routingResponseType = invocable.getRoutingResponseType(); this.extended = originalMethod.isExtended(); Method handlerMethod = invocable.getDefinitionMethod(); MethodHandler handler = invocable.getHandler(); if (handler.isClassBased()) { handledBy(handler.getHandlerClass(), handlerMethod); } else { handledBy(handler.getHandlerInstance(), handlerMethod); } } /** * Set the associated HTTP method name. * * @param name HTTP method name. * @return updated builder object. */ public Builder httpMethod(String name) { this.httpMethod = name; return this; } /** * Add produced media types supported by the component. * * @param types produced media types. * @return updated builder object. */ public Builder produces(String... types) { return produces(MediaTypes.createFrom(types)); } /** * Add produced media types supported by the component. * * @param types produced media types. * @return updated builder object. */ public Builder produces(MediaType... types) { return produces(Arrays.asList(types)); } /** * Add produced media types supported by the component. * * @param types produced media types. * @return updated builder object. */ public Builder produces(Collection types) { this.producedTypes.addAll(types); return this; } /** * Add consumed media types supported by the component. * * @param types consumed media types. * @return updated builder object. */ public Builder consumes(String... types) { return consumes(MediaTypes.createFrom(types)); } /** * Add consumed media types supported by the component. * * @param types consumed media types. * @return updated builder object. */ public Builder consumes(MediaType... types) { return consumes(Arrays.asList(types)); } /** * Add consumed media types supported by the component. * * @param types consumed media types. * @return updated builder object. */ public Builder consumes(Collection types) { this.consumedTypes.addAll(types); return this; } /** * Adds name bindings. The passed annotation types not annotated with {@link javax.ws.rs.NameBinding} * meta-annotation will be ignored. * * @param nameBindings collection of name binding annotation types. * @return updated builder object. */ public Builder nameBindings(final Collection> nameBindings) { for (Class nameBinding : nameBindings) { if (nameBinding.getAnnotation(NameBinding.class) != null) { this.nameBindings.add(nameBinding); } } return this; } /** * Adds name bindings. The passed annotation types not annotated with {@link javax.ws.rs.NameBinding} * meta-annotation will be ignored. * * @param nameBindings name binding annotation types. * @return updated builder object. */ public Builder nameBindings(final Class... nameBindings) { return nameBindings(Arrays.asList(nameBindings)); } /** * Adds name bindings. The passed annotations not annotated with {@link javax.ws.rs.NameBinding} * meta-annotation will be ignored. * * @param nameBindings name binding annotations. * @return updated builder object. */ public Builder nameBindings(final Annotation... nameBindings) { return nameBindings(Collections2.transform(Arrays.asList(nameBindings), new Function>() { @Override public Class apply(Annotation input) { return input.annotationType(); } }) ); } /** * Mark the component for suspending. *

* An invocation of a component (resource or sub-resource method) marked * for suspending will be automatically suspended by the Jersey runtime. * * @param timeout suspend timeout value. * @param unit suspend timeout time unit. * @return updated builder object. */ public Builder suspended(long timeout, TimeUnit unit) { suspended = true; suspendTimeout = timeout; suspendTimeoutUnit = unit; return this; } /** * Set the managed async required flag on the method model to {@code true}. * * @return updated builder object. */ public Builder managedAsync() { managedAsync = true; return this; } /** * If set to {@code true}, the parameter values will not be automatically * decoded. *

* Defaults to {@code false}. * * @param value {@code true} if the automatic parameter decoding should be * disabled, false otherwise. * @return updated builder object. * @see javax.ws.rs.Encoded */ public Builder encodedParameters(boolean value) { encodedParams = value; return this; } /** * Define a resource method handler binding. * * @param handlerClass concrete resource method handler class. * @param method method that will be executed as a resource method. The parameters initializes * {@link org.glassfish.jersey.server.model.Invocable#getDefinitionMethod() invocable * definition method}. * @return updated builder object. */ public Builder handledBy(Class handlerClass, Method method) { this.handlerInstance = null; this.handlerClass = handlerClass; this.definitionMethod = method; return this; } /** * Define a resource method handler binding. * * @param handlerInstance concrete resource method handler instance. * @param method handling method. * @return updated builder object. */ public Builder handledBy(Object handlerInstance, Method method) { this.handlerClass = null; this.handlerInstance = handlerInstance; this.definitionMethod = method; return this; } /** * Define an inflector-based resource method handler binding. * * @param inflector inflector handling the resource method. * @return updated builder object. */ public Builder handledBy(Inflector inflector) { return handledBy(inflector, Invocable.APPLY_INFLECTOR_METHOD); } /** * Define an inflector-based resource method handler binding. * * @param inflectorClass class of the inflector handling the resource method. * @return updated builder object. */ public Builder handledBy(Class inflectorClass) { return handledBy(inflectorClass, Invocable.APPLY_INFLECTOR_METHOD); } /** * Define a specific method of the handling class that will be executed. If the method * is not defined then the method will be equal to the method initialized by * one of the {@code handledBy()} builder methods. * * @param handlingMethod specific handling method. * @return updated builder object. */ public Builder handlingMethod(final Method handlingMethod) { this.handlingMethod = handlingMethod; return this; } /** * Define the response entity type used during the routing for * selection of the resource methods. If this method is not called then * the {@link Invocable#getRoutingResponseType()} will be equal to * {@link org.glassfish.jersey.server.model.Invocable#getResponseType()} which * is the default configuration state. * * @param routingResponseType Routing response type. * @return updated builder object. * @see org.glassfish.jersey.server.model.Invocable#getRoutingResponseType() */ public Builder routingResponseType(Type routingResponseType) { this.routingResponseType = routingResponseType; return this; } /** * Get the flag indicating whether the resource method is extended or is a core of exposed RESTful API. * The method defines the * flag available at {@link org.glassfish.jersey.server.model.ResourceMethod#isExtended()}. *

* Extended resource model components are helper components that are not considered as a core of a * RESTful API. These can be for example {@code OPTIONS} {@link ResourceMethod resource methods} * added by {@link org.glassfish.jersey.server.model.ModelProcessor model processors} * or {@code application.wadl} resource producing the WADL. Both resource are rather supportive * than the core of RESTful API. *

* * @param extended If {@code true} then resource method is marked as extended. * @return updated builder object. * @see org.glassfish.jersey.server.model.ExtendedResource * * @since 2.5.1 */ public Builder extended(boolean extended) { this.extended = extended; return this; } /** * Build the resource method model and register it with the parent * {@link Resource.Builder Resource.Builder}. * * @return new resource method model. */ public ResourceMethod build() { final Data methodData = new Data( httpMethod, consumedTypes, producedTypes, managedAsync, suspended, suspendTimeout, suspendTimeoutUnit, createInvocable(), nameBindings, parent.isExtended() || extended); parent.onBuildMethod(this, methodData); return new ResourceMethod(null, methodData); } private Invocable createInvocable() { assert handlerClass != null || handlerInstance != null; final MethodHandler handler; if (handlerClass != null) { handler = MethodHandler.create(handlerClass, encodedParams); } else { // instance based handler = MethodHandler.create(handlerInstance); } return Invocable.create(handler, definitionMethod, handlingMethod, encodedParams, routingResponseType); } } /** * Immutable resource method data. */ /* package */ static class Data { // JAX-RS method type private final JaxrsType type; // HttpMethod private final String httpMethod; // Consuming & Producing private final List consumedTypes; private final List producedTypes; // SuspendableComponent private final boolean managedAsync; private final boolean suspended; private final long suspendTimeout; private final TimeUnit suspendTimeoutUnit; // Invocable private final Invocable invocable; // NameBound private final Collection> nameBindings; private final boolean extended; private Data(final String httpMethod, final Collection consumedTypes, final Collection producedTypes, boolean managedAsync, final boolean suspended, final long suspendTimeout, final TimeUnit suspendTimeoutUnit, final Invocable invocable, final Collection> nameBindings, final boolean extended) { this.managedAsync = managedAsync; this.type = JaxrsType.classify(httpMethod); this.httpMethod = (httpMethod == null) ? httpMethod : httpMethod.toUpperCase(); this.consumedTypes = Collections.unmodifiableList(Lists.newArrayList(consumedTypes)); this.producedTypes = Collections.unmodifiableList(Lists.newArrayList(producedTypes)); this.invocable = invocable; this.suspended = suspended; this.suspendTimeout = suspendTimeout; this.suspendTimeoutUnit = suspendTimeoutUnit; this.nameBindings = Collections.unmodifiableCollection(Lists.newArrayList(nameBindings)); this.extended = extended; } /** * Get the JAX-RS method type. * * @return the JAX-RS method type. */ /* package */ JaxrsType getType() { return type; } /** * Get the associated HTTP method. *

* May return {@code null} in case the method represents a sub-resource * locator. *

* * @return the associated HTTP method, or {@code null} in case this method * represents a sub-resource locator. */ /* package */ String getHttpMethod() { return httpMethod; } /** * Get consumable media types. * * @return consumable media types. */ /* package */ List getConsumedTypes() { return consumedTypes; } /** * Get produced media types. * * @return produced media types. */ /* package */ List getProducedTypes() { return producedTypes; } /** * Flag indicating whether managed async support declared on the method. * * @return {@code true} if managed async support is declared on the method, {@code false} otherwise. */ /* package */ boolean isManagedAsync() { return managedAsync; } /** * Flag indicating whether the method requires injection of suspended response context. * * @return {@code true} if the method requires injection of suspended response context, {@code false} otherwise. */ /* package */ boolean isSuspended() { return suspended; } /** * Get the suspended timeout value for the method. * * @return the suspended timeout value for the method. */ /* package */ long getSuspendTimeout() { return suspendTimeout; } /** * Get the suspended timeout time unit for the method. * * @return the suspended timeout time unit for the method. */ /* package */ TimeUnit getSuspendTimeoutUnit() { return suspendTimeoutUnit; } /** * Get the invocable method model. * * @return invocable method model. */ /* package */ Invocable getInvocable() { return invocable; } /** * Get the flag indicating whether the resource method is extended or is a core of exposed RESTful API. * * @return {@code true} if resource is extended. */ /* package */ boolean isExtended() { return extended; } /** * Get the collection of name bindings attached to this method. * * @return collection of name binding annotation types attached to the method. */ /* package */ Collection> getNameBindings() { return nameBindings; } @Override public String toString() { return "httpMethod=" + httpMethod + ", consumedTypes=" + consumedTypes + ", producedTypes=" + producedTypes + ", suspended=" + suspended + ", suspendTimeout=" + suspendTimeout + ", suspendTimeoutUnit=" + suspendTimeoutUnit + ", invocable=" + invocable + ", nameBindings=" + nameBindings; } } /** * Transform a collection of resource method data into resource method models. * * @param parent parent resource model. * @param list resource method data collection. * @return transformed resource method models. */ static List transform(final Resource parent, final List list) { return Lists.transform(list, new Function() { @Override public ResourceMethod apply(Data data) { return (data == null) ? null : new ResourceMethod(parent, data); } }); } private final Data data; private final Resource parent; /** * Create new resource method model instance. * * @param parent parent resource model. * @param data resource method model data. */ ResourceMethod(final Resource parent, final Data data) { this.parent = parent; this.data = data; } /** * Get model data represented by this resource method. * * @return model data represented by this resource method. */ /* package */ Data getData() { return data; } /** * Get the parent resource for this resource method model. *

* May return {@code null} in case the resource method is not bound to an existing resource. * This is typical for resource method models returned directly from the * {@link ResourceMethod.Builder#build() ResourceMethod.Builder.build()} method. *

* * @return parent resource, or {@code null} if there is no parent resource associated with the method. * @since 2.1 */ public Resource getParent() { return parent; } /** * Get the JAX-RS method type. * * @return the JAX-RS method type. */ public JaxrsType getType() { return data.getType(); } /** * Get the associated HTTP method. *

* May return {@code null} in case the method represents a sub-resource * locator. *

* * @return the associated HTTP method, or {@code null} in case this method * represents a sub-resource locator. */ public String getHttpMethod() { return data.getHttpMethod(); } /** * Get the invocable method model. * * @return invocable method model. */ public Invocable getInvocable() { return data.getInvocable(); } /** * Get the flag indicating whether the resource method is extended or is a core of exposed RESTful API. *

* Extended resource model components are helper components that are not considered as a core of a * RESTful API. These can be for example {@code OPTIONS} resource methods * added by {@link org.glassfish.jersey.server.model.ModelProcessor model processors} * or {@code application.wadl} resource producing the WADL. Both resource are rather supportive * than the core of RESTful API. *

*

* If not set the resource will not be defined as extended by default. *

* * @return {@code true} if the method is extended. * @see org.glassfish.jersey.server.model.ExtendedResource * * @since 2.5.1 */ public boolean isExtended() { return data.extended; } // Consuming @Override public List getConsumedTypes() { return data.getConsumedTypes(); } // Producing @Override public List getProducedTypes() { return data.getProducedTypes(); } // Suspendable @Override public long getSuspendTimeout() { return data.getSuspendTimeout(); } @Override public TimeUnit getSuspendTimeoutUnit() { return data.getSuspendTimeoutUnit(); } @Override public boolean isSuspendDeclared() { return data.isSuspended(); } @Override public boolean isManagedAsyncDeclared() { return data.isManagedAsync(); } // ResourceModelComponent @Override public List getComponents() { return Arrays.asList(data.getInvocable()); } @Override public void accept(ResourceModelVisitor visitor) { visitor.visitResourceMethod(this); } // NameBound @Override public boolean isNameBound() { return !data.getNameBindings().isEmpty(); } @Override public Collection> getNameBindings() { return data.getNameBindings(); } @Override public String toString() { return "ResourceMethod{" + data.toString() + '}'; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy