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.6
Show newest version
/*
 * Copyright (c) 2010, 2020 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.server.model;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import jakarta.ws.rs.NameBinding;
import jakarta.ws.rs.container.AsyncResponse;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.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;

/**
 * Model of a method available on a resource. Covers resource method, sub-resource
 * method and sub-resource locator.
 *
 * @author Marek Potociar
 */
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 sse; private boolean suspended; private long suspendTimeout; private TimeUnit suspendTimeoutUnit; // Invocable private Class handlerClass; private Object handlerInstance; private final Collection handlerParameters; // method (can be also interface method). Specific method to execute is defined by handlingMethod private Method definitionMethod; // this can be either equal to definitionMethod or child of 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 = new LinkedHashSet<>(); this.producedTypes = new LinkedHashSet<>(); this.suspended = false; this.suspendTimeout = AsyncResponse.NO_TIMEOUT; this.suspendTimeoutUnit = TimeUnit.MILLISECONDS; this.handlerParameters = new LinkedList<>(); this.encodedParams = false; this.nameBindings = new LinkedHashSet<>(); } /** * Create a builder from an existing resource method model. * * @param parent parent resource model builder. * @param originalMethod existing resource method model to create the builder from. */ /* package */ Builder(final Resource.Builder parent, ResourceMethod originalMethod) { this.parent = parent; this.consumedTypes = new LinkedHashSet<>(originalMethod.getConsumedTypes()); this.producedTypes = new LinkedHashSet<>(originalMethod.getProducedTypes()); this.suspended = originalMethod.isSuspendDeclared(); this.suspendTimeout = originalMethod.getSuspendTimeout(); this.suspendTimeoutUnit = originalMethod.getSuspendTimeoutUnit(); this.handlerParameters = new LinkedHashSet<>(originalMethod.getInvocable().getHandler().getParameters()); 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 jakarta.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 jakarta.ws.rs.NameBinding} * meta-annotation will be ignored. * * @param nameBindings name binding annotation types. * @return updated builder object. */ @SafeVarargs public final Builder nameBindings(final Class... nameBindings) { return nameBindings(Arrays.asList(nameBindings)); } /** * Adds name bindings. The passed annotations not annotated with {@link jakarta.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( Arrays.stream(nameBindings) .map((Function>) Annotation::annotationType) .collect(Collectors.toList()) ); } /** * 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 SSE flag on the method model to {@code true}. * * @return updated builder object. */ public Builder sse() { sse = true; 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 jakarta.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); } /** * Parameters defined on the handler (i.e. not in the handling method), e.g. via property setters or fields. * * @param parameters handler parameters to be added to the set of handler parameters for the method. * @return updated builder object. * @since 2.20 */ public Builder handlerParameters(Collection parameters) { this.handlerParameters.addAll(parameters); return this; } /** * 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, sse, 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, handlerParameters); } else { // instance based handler = MethodHandler.create(handlerInstance, handlerParameters); } 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 boolean sse; 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, boolean sse, 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(Locale.ROOT); this.consumedTypes = Collections.unmodifiableList(new ArrayList<>(consumedTypes)); this.producedTypes = Collections.unmodifiableList(new ArrayList<>(producedTypes)); this.invocable = invocable; this.suspended = suspended; this.sse = sse; this.suspendTimeout = suspendTimeout; this.suspendTimeoutUnit = suspendTimeoutUnit; this.nameBindings = Collections.unmodifiableCollection(new ArrayList<>(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; } /** * Flag indicating whether the method requires injection of Sse Event Sink. * * @return {@code true} if the method requires injection of Sse Event Sink, {@code false} otherwise. */ /* package */ boolean isSse() { return sse; } /** * 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 list.stream() .map(data1 -> (data1 == null) ? null : new ResourceMethod(parent, data1)) .collect(Collectors.toList()); } 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(); } /** * Check whether the resource method will be producing Server-sent event stream. * * @return {@code true} if the resource method produces Server-sent event stream, {@code false} otherwise. */ public boolean isSse() { return data.isSse(); } @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