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

org.glassfish.jersey.server.model.Resource 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-2013 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.reflect.Modifier;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.ws.rs.Path;

import org.glassfish.jersey.Severity;
import org.glassfish.jersey.internal.Errors;
import org.glassfish.jersey.internal.util.collection.Value;
import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.server.internal.LocalizationMessages;
import org.glassfish.jersey.server.model.internal.ModelHelper;
import org.glassfish.jersey.uri.PathPattern;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
 * Model of a single resource component.
 * 

* Resource component model represents a collection of {@link ResourceMethod methods} * grouped under the same parent request path template. {@code Resource} class is also * the main entry point to the programmatic resource modeling API that provides ability * to programmatically extend the existing JAX-RS annotated resource classes or build * new resource models that may be utilized by Jersey runtime. *

*

* For example: *

 * @Path("hello")
 * public class HelloResource {
 *      @GET
 *      @Produces("text/plain")
 *      public String sayHello() {
 *          return "Hello!";
 *      }
 * }
 *
 * ...
 *
 * // Register the annotated resource.
 * ResourceConfig resourceConfig = new ResourceConfig(HelloResource.class);
 *
 * // Add new "hello2" resource using the annotated resource class
 * // and overriding the resource path.
 * Resource.Builder resourceBuilder =
 *         Resource.builder(HelloResource.class, new LinkedList<ResourceModelIssue>())
 *         .path("hello2");
 *
 * // Add a new (virtual) sub-resource method to the "hello2" resource.
 * resourceBuilder.addChildResource("world").addMethod("GET")
 *         .produces("text/plain")
 *         .handledBy(new Inflector<Request, String>() {
 *                 @Override
 *                 public String apply(Request request) {
 *                     return "Hello World!";
 *                 }
 *         });
 *
 * // Register the new programmatic resource in the application's configuration.
 * resourceConfig.registerResources(resourceBuilder.build());
 * 
* The following table illustrates the supported requests and provided responses * for the application configured in the example above. * * * * * * * * * * * * * *
RequestResponseMethod invoked
{@code "GET /hello"}{@code "Hello!"}{@code HelloResource.sayHello()}
{@code "GET /hello2"}{@code "Hello!"}{@code HelloResource.sayHello()}
{@code "GET /hello2/world"}{@code "Hello World!"}{@code Inflector.apply()}
*

* * @author Marek Potociar (marek.potociar at oracle.com) * @author Miroslav Fuksa (miroslav.fuksa at oracle.com) */ public final class Resource implements Routed, ResourceModelComponent { /** * Immutable resource data holder. */ private static class Data { private final List names; private final String path; private final PathPattern pathPattern; private final List resourceMethods; private final ResourceMethod.Data locator; private final List childResources; private final Set> handlerClasses; private final Set handlerInstances; private final boolean extended; /** * Create a new immutable resource data holder from the supplied parameters. * * @param names resource names. * @param path resource path. * @param resourceMethods child resource methods. * @param locator child resource locator. * @param childResources child sub-resources. * @param handlerClasses handler classes handling the resource methods. * @param handlerInstances handler instances handling the resource methods. * @param extended */ private Data( final List names, final String path, final List resourceMethods, final ResourceMethod.Data locator, final List childResources, final Set> handlerClasses, final Set handlerInstances, boolean extended) { this.extended = extended; this.names = immutableCopy(names); this.path = path; this.pathPattern = (path == null || path.isEmpty()) ? PathPattern.OPEN_ROOT_PATH_PATTERN : new PathPattern(path, PathPattern.RightHandPath.capturingZeroOrMoreSegments); this.resourceMethods = Resource.immutableCopy(resourceMethods); this.locator = locator; this.childResources = Collections.unmodifiableList(childResources); // no need to deep-copy the list this.handlerClasses = Resource.immutableCopy(handlerClasses); this.handlerInstances = Resource.immutableCopy(handlerInstances); } @Override public String toString() { return "Resource{" + ((path == null) ? "[unbound], " : "\"" + path + "\", ") + childResources.size() + " child resources, " + resourceMethods.size() + " resource methods, " + (locator == null ? "0" : "1") + " sub-resource locator, " + handlerClasses.size() + " method handler classes, " + handlerInstances.size() + " method handler instances" + '}'; } } /** * Resource model component builder. */ public static final class Builder { private List names; private String path; private final Set methodBuilders; private final Set childResourceBuilders; private final List childResources; private final List resourceMethods; private ResourceMethod.Data resourceLocator; private final Set> handlerClasses; private final Set handlerInstances; private final Resource.Builder parentResource; private boolean extended; private Builder(final Resource.Builder parentResource) { this.methodBuilders = Sets.newIdentityHashSet(); this.childResourceBuilders = Sets.newIdentityHashSet(); this.childResources = Lists.newLinkedList(); this.resourceMethods = Lists.newLinkedList(); this.handlerClasses = Sets.newIdentityHashSet(); this.handlerInstances = Sets.newIdentityHashSet(); this.parentResource = parentResource; name("[unnamed]"); } private Builder(final String path) { this((Resource.Builder) null); path(path); } private Builder(final String path, final Resource.Builder parentResource) { this(parentResource); this.path = path; } private Builder() { this((Resource.Builder) null); } private boolean isEmpty() { return this.path == null && methodBuilders.isEmpty() && childResourceBuilders.isEmpty() && resourceMethods.isEmpty() && childResources.isEmpty() && resourceLocator == null; } /** * Define a new name of the built resource. *

* The resource model name is typically used for reporting * purposes (e.g. validation etc.). * * @param name new name of the resource. * @return updated builder object. * @see org.glassfish.jersey.server.model.Resource#getName() */ public Builder name(final String name) { this.names = Lists.newArrayList(name); return this; } /** * Define a new path for the built resource. *

* NOTE: Invoking this method marks a resource as a root resource. * * @param path new path for the resource. * @return updated builder object. */ public Builder path(final String path) { this.path = path; return this; } /** * Add a new method model to the resource for processing requests of * the specified HTTP method. *

* The returned builder is automatically bound to the the resource. It is * not necessary to invoke the {@link ResourceMethod.Builder#build() build()} * method on the method builder after setting all the data. This will be * done automatically when the resource is built. * * @param httpMethod HTTP method that will be processed by the method. * @return a new resource method builder. */ public ResourceMethod.Builder addMethod(final String httpMethod) { ResourceMethod.Builder builder = new ResourceMethod.Builder(this); methodBuilders.add(builder); return builder.httpMethod(httpMethod); } /** * Add a new arbitrary method model to the resource. *

* The returned builder is automatically bound to the the resource. It is * not necessary to invoke the {@link ResourceMethod.Builder#build() build()} * method on the method builder after setting all the data. This will be * done automatically when the resource is built. * * @return a new resource method builder. */ public ResourceMethod.Builder addMethod() { ResourceMethod.Builder builder = new ResourceMethod.Builder(this); methodBuilders.add(builder); return builder; } /** * Add a new method model that is a copy of the given {@code resourceMethod}. *

* The returned builder is automatically bound to the the resource. It is * not necessary to invoke the {@link ResourceMethod.Builder#build() build()} * method on the method builder after setting all the data. This will be * done automatically when the resource is built. * * @param resourceMethod The resource method based on which the new method builder * should be created. * * @return a new resource method builder. */ public ResourceMethod.Builder addMethod(ResourceMethod resourceMethod) { ResourceMethod.Builder builder = new ResourceMethod.Builder(this, resourceMethod); methodBuilders.add(builder); return builder; } /** * Add a new child resource to the resource. *

* The returned builder is automatically bound to the the resource. It is * not necessary to invoke the {@link Resource.Builder#build() build()} * method on the resource builder after setting all the data. This will be * done automatically when the resource is built. * * @param relativePath The path of the new child resource relative to this resource. * @return child resource builder. */ public Builder addChildResource(String relativePath) { if (this.parentResource != null) { throw new IllegalStateException(LocalizationMessages.RESOURCE_ADD_CHILD_ALREADY_CHILD()); } final Builder resourceBuilder = new Builder(relativePath, this); childResourceBuilders.add(resourceBuilder); return resourceBuilder; } /** * Add an existing Resource as a child resource of current resource. * * @param resource Resource to be added as child resource. */ public void addChildResource(Resource resource) { this.childResources.add(resource.data); } /** * Merge methods from a given resource model into this resource model builder. * * @param resource to be merged into this resource model builder. * @return updated builder object. */ public Builder mergeWith(final Resource resource) { mergeWith(resource.data); return this; } /** * Set the flag indicating whether the resource is extended or is a core of exposed RESTful API. * The method defines the * flag available at {@link org.glassfish.jersey.server.model.Resource#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. *

*

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

* * @param extended If {@code true} then resource 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; } /** * Get the flag indicating whether the resource method is extended or is a core of exposed RESTful API. * * @return {@code true} if the method is extended. */ /* package */ boolean isExtended() { return extended; } private Builder mergeWith(final Resource.Data resourceData) { this.resourceMethods.addAll(resourceData.resourceMethods); this.childResources.addAll(resourceData.childResources); if (resourceLocator != null && resourceData.locator != null) { Errors.processWithException(new Runnable() { @Override public void run() { Errors.error( this, LocalizationMessages.RESOURCE_MERGE_CONFLICT_LOCATORS(Builder.this, resourceData, path), Severity.FATAL); } }); } else if (resourceData.locator != null) { this.resourceLocator = resourceData.locator; } this.handlerClasses.addAll(resourceData.handlerClasses); this.handlerInstances.addAll(resourceData.handlerInstances); this.names.addAll(resourceData.names); return this; } /** * Merge methods from a given resource model builder into this resource model * builder. *

* NOTE: Any "open" method builders in the supplied {@code resourceBuilder} that have * not been {@link org.glassfish.jersey.server.model.ResourceMethod.Builder#build() * explicitly converted to method models} will be closed as part of this merge operation * before merging the resource builder instances. *

* * @param resourceBuilder to be merged into this resource model builder. * @return updated builder object. */ public Builder mergeWith(final Builder resourceBuilder) { resourceBuilder.processMethodBuilders(); this.resourceMethods.addAll(resourceBuilder.resourceMethods); this.childResources.addAll(resourceBuilder.childResources); if (Resource.Builder.this.resourceLocator != null && resourceBuilder.resourceLocator != null) { Errors.processWithException(new Runnable() { @Override public void run() { Errors.warning(this, LocalizationMessages.RESOURCE_MERGE_CONFLICT_LOCATORS(Resource.Builder.this, resourceBuilder, path)); } }); } else if (resourceBuilder.resourceLocator != null) { this.resourceLocator = resourceBuilder.resourceLocator; } this.handlerClasses.addAll(resourceBuilder.handlerClasses); this.handlerInstances.addAll(resourceBuilder.handlerInstances); this.names.addAll(resourceBuilder.names); return this; } /** * Called when a new resource, sub-resource and sub-resource locator method * was built and should be registered with the resource builder. *

* This is a friend call-back API exposed for a use by a {@link ResourceMethod.Builder * ResourceMethod.Builder}. *

* * @param builder builder instance that built the method. * @param methodData new resource, sub-resource or sub-resource locator */ void onBuildMethod(ResourceMethod.Builder builder, ResourceMethod.Data methodData) { Preconditions.checkState(methodBuilders.remove(builder), "Resource.Builder.onBuildMethod() invoked from a resource method builder " + "that is not registered in the resource builder instance."); switch (methodData.getType()) { case RESOURCE_METHOD: resourceMethods.add(methodData); break; case SUB_RESOURCE_LOCATOR: if (resourceLocator != null) { Errors.processWithException(new Runnable() { @Override public void run() { Errors.error( this, LocalizationMessages.AMBIGUOUS_SRLS(this, path), Severity.FATAL); } }); } resourceLocator = methodData; break; } final MethodHandler methodHandler = methodData.getInvocable().getHandler(); if (methodHandler.isClassBased()) { handlerClasses.add(methodHandler.getHandlerClass()); } else { handlerInstances.add(methodHandler.getHandlerInstance()); } } private void onBuildChildResource(Builder childResourceBuilder, Resource.Data childResourceData) { Preconditions.checkState(childResourceBuilders.remove(childResourceBuilder), "Resource.Builder.onBuildChildResource() invoked from a resource builder " + "that is not registered in the resource builder instance as a child resource builder."); childResources.add(childResourceData); } private List mergeResources(List resources) { List mergedResources = Lists.newArrayList(); for (int i = 0; i < resources.size(); i++) { Resource.Data outer = resources.get(i); Resource.Builder builder = null; for (int j = i + 1; j < resources.size(); j++) { Resource.Data inner = resources.get(j); if (outer.path.equals(inner.path)) { if (builder == null) { builder = Resource.builder(outer); } builder.mergeWith(inner); resources.remove(j); j--; } } if (builder == null) { mergedResources.add(outer); } else { mergedResources.add(builder.buildResourceData()); } } return mergedResources; } private Data buildResourceData() { if (parentResource != null && parentResource.isExtended()) { this.extended = true; } processMethodBuilders(); processChildResourceBuilders(); final List mergedChildResources = mergeResources(childResources); Set> classes = Sets.newHashSet(handlerClasses); Set instances = Sets.newHashSet(handlerInstances); for (Data childResource : mergedChildResources) { classes.addAll(childResource.handlerClasses); instances.addAll(childResource.handlerInstances); } if (areAllMembersExtended(mergedChildResources)) { extended = true; } final Data resourceData = new Data( names, path, resourceMethods, resourceLocator, mergedChildResources, classes, instances, extended); if (parentResource != null) { parentResource.onBuildChildResource(this, resourceData); } return resourceData; } private boolean areAllMembersExtended(List mergedChildResources) { boolean allExtended = true; for (ResourceMethod.Data resourceMethod : resourceMethods) { if (!resourceMethod.isExtended()) { allExtended = false; } } if (resourceLocator != null && !resourceLocator.isExtended()) { allExtended = false; } for (Data childResource : mergedChildResources) { if (!childResource.extended) { allExtended = false; } } return allExtended; } /** * Build a new resource model. * * @return new (immutable) resource model. */ public Resource build() { final Data resourceData = buildResourceData(); return new Resource(null, resourceData); } private void processMethodBuilders() { // We have to iterate the set this way to prevent ConcurrentModificationExceptions // caused by the nested invocation of Set.remove(...) in Resource.Builder.onBuildMethod(...). while (!methodBuilders.isEmpty()) { methodBuilders.iterator().next().build(); } } private void processChildResourceBuilders() { // We have to iterate the set this way to prevent ConcurrentModificationExceptions // caused by the nested invocation of Set.remove(...) in Resource.Builder.onBuildChildResource(...). while (!childResourceBuilders.isEmpty()) { childResourceBuilders.iterator().next().build(); } } } /** * Get a new unbound resource model builder. * * @return new unbound resource model builder. * @see Resource.Builder#path(java.lang.String) */ public static Builder builder() { return new Builder(); } /** * Get a new resource model builder for a resource bound to a given path. * * @param path resource path. * @return new resource model builder. * @see Resource.Builder#path(java.lang.String) */ public static Builder builder(final String path) { return new Builder(path); } /** * Creates a {@link Builder resource builder} instance from the list of {@code resource} which can be merged * into a single resource. It must be possible to merge the {@code resources} into a single valid resource. * For example all resources must have the same {@link Resource#getPath() path}, they cannot have ambiguous methods * on the same path, etc. * * @param resources Resources with the same path. * @return Resource builder initialized from merged resources. */ public static Builder builder(List resources) { if (resources == null || resources.isEmpty()) { return builder(); } final Iterator it = resources.iterator(); Resource.Data resourceData = it.next().data; Builder builder = Resource.builder(resourceData); String path = resourceData.path; while (it.hasNext()) { resourceData = it.next().data; if ((resourceData.path == null && path == null) || (path != null && path.equals(resourceData.path))) { builder.mergeWith(resourceData); } else { throw new IllegalArgumentException(LocalizationMessages.ERROR_RESOURCES_CANNOT_MERGE()); } } return builder; } /** * Create a resource model builder initialized by introspecting an annotated * JAX-RS resource class. * * @param resourceClass resource class to be modelled. * @return resource model builder initialized by the class or {@code null} if the * class does not represent a resource. */ public static Builder builder(Class resourceClass) { return builder(resourceClass, false); } /** * Create a resource model builder initialized by introspecting an annotated * JAX-RS resource class. * * @param resourceClass resource class to be modelled. * @param disableValidation if set to {@code true}, then any model validation checks will be disabled. * @return resource model builder initialized by the class or {@code null} if the * class does not represent a resource. */ public static Builder builder(Class resourceClass, boolean disableValidation) { final Builder builder = new IntrospectionModeller(resourceClass, disableValidation).createResourceBuilder(); return builder.isEmpty() ? null : builder; } /** * Create a resource model initialized by introspecting an annotated * JAX-RS resource class. * * @param resourceClass resource class to be modelled. * @return resource model initialized by the class or {@code null} if the * class does not represent a resource. */ public static Resource from(Class resourceClass) { return from(resourceClass, false); } /** * Create a resource model initialized by introspecting an annotated * JAX-RS resource class. * * @param resourceClass resource class to be modelled. * @param disableValidation if set to {@code true}, then any model validation checks will be disabled. * @return resource model initialized by the class or {@code null} if the * class does not represent a resource. */ public static Resource from(Class resourceClass, boolean disableValidation) { final Builder builder = new IntrospectionModeller(resourceClass, disableValidation).createResourceBuilder(); return builder.isEmpty() ? null : builder.build(); } /** * Check if the class is acceptable as a JAX-RS provider or resource. *

* Method returns {@code false} if the class is either *

    *
  • abstract
  • *
  • interface
  • *
  • annotation
  • *
  • primitive
  • *
  • local class
  • *
  • non-static member class
  • *
* * @param c class to be checked. * @return {@code true} if the class is an acceptable JAX-RS provider or * resource, {@code false} otherwise. */ public static boolean isAcceptable(Class c) { return !((c.getModifiers() & Modifier.ABSTRACT) != 0 || c.isPrimitive() || c.isAnnotation() || c.isInterface() || c.isLocalClass() || (c.isMemberClass() && (c.getModifiers() & Modifier.STATIC) == 0)); } /** * Get the resource class {@link Path @Path} annotation. *

* May return {@code null} in case there is no {@code @Path} annotation on the resource. * * @param resourceClass resource class. * @return {@code @Path} annotation instance if present on the resource class (i.e. * the class is a root resource class), or {@code null} otherwise. */ public static Path getPath(Class resourceClass) { return ModelHelper.getAnnotatedResourceClass(resourceClass).getAnnotation(Path.class); } /** * Get a new resource model builder initialized from a given resource model. * * @param resource resource model initializing the resource builder. * @return new resource model builder. */ public static Builder builder(Resource resource) { return builder(resource.data); } private static Builder builder(Resource.Data resourceData) { final Builder b; if (resourceData.path == null) { b = new Builder(); } else { b = new Builder(resourceData.path); } b.resourceMethods.addAll(resourceData.resourceMethods); b.childResources.addAll(resourceData.childResources); b.resourceLocator = resourceData.locator; b.handlerClasses.addAll(resourceData.handlerClasses); b.handlerInstances.addAll(resourceData.handlerInstances); b.names.addAll(resourceData.names); return b; } private static List transform(final Resource parent, final List list) { return Lists.transform(list, new Function() { @Override public Resource apply(Data data) { return new Resource(parent, data); } }); } private static List immutableCopy(List list) { return list.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(Lists.newArrayList(list)); } private static Set immutableCopy(Set set) { if (set.isEmpty()) { return Collections.emptySet(); } final Set result = Sets.newIdentityHashSet(); result.addAll(set); return set; } private final Resource parent; private final Data data; private final Value name; private final List resourceMethods; private final ResourceMethod locator; private final List childResources; private Resource(final Resource parent, final Data data) { this.parent = parent; this.data = data; this.name = Values.lazy(new Value() { @Override public String get() { if (data.names.size() == 1) { return data.names.get(0); } else { // return merged name StringBuilder nameBuilder = new StringBuilder("Merge of "); nameBuilder.append(data.names.toString()); return nameBuilder.toString(); } } }); this.resourceMethods = immutableCopy(ResourceMethod.transform(Resource.this, data.resourceMethods)); this.locator = data.locator == null ? null : new ResourceMethod(Resource.this, data.locator); this.childResources = immutableCopy(Resource.transform(Resource.this, data.childResources)); } @Override public String getPath() { return data.path; } @Override public PathPattern getPathPattern() { return data.pathPattern; } /** * Get the parent resource for this resource model or {@code null} in case this * resource is a top-level resource and does not have a parent. * * @return parent resource or {@code null} if the resource does not have a parent. * @since 2.1 */ public Resource getParent() { return parent; } /** * Get the resource name. *

* If the resource was constructed from a JAX-RS annotated resource class, * the resource name will be set to the {@link Class#getName() fully-qualified name} * of the resource class. * * @return reference JAX-RS resource handler class. */ public String getName() { return name.get(); } /** * Return a list of resource names. * * @return a list of resource names. */ public List getNames() { return data.names; } /** * Provides a non-null list of resource methods available on the resource. * * @return non-null abstract resource method list. */ public List getResourceMethods() { return resourceMethods; } /** * Provides a resource locator available on the resource. * * @return Resource locator if it is present, null otherwise. */ public ResourceMethod getResourceLocator() { return locator; } /** * Provides resource methods and resource locator are available on the resource. The list is ordered so that resource * methods are positioned first before resource locator. * * @return List of resource methods and resource locator. */ public List getAllMethods() { final LinkedList methodsAndLocators = Lists.newLinkedList(getResourceMethods()); final ResourceMethod loc = getResourceLocator(); if (loc != null) { methodsAndLocators.add(loc); } return methodsAndLocators; } /** * Returns the list of child resources available on this resource. * * @return Non-null list of child resources (may be empty). */ public List getChildResources() { return childResources; } /** * Get the method handler classes for the resource methods registered on the resource. * * @return resource method handler classes. */ public Set> getHandlerClasses() { return data.handlerClasses; } /** * Get the method handler (singleton) instances for the resource methods registered * on the resource. * * @return resource method handler instances. */ public Set getHandlerInstances() { return data.handlerInstances; } @Override public void accept(ResourceModelVisitor visitor) { if (getParent() == null) { visitor.visitResource(this); } else { visitor.visitChildResource(this); } } /** * Get the flag indicating whether the resource 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} {@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. *

* * @return {@code true} if the resource is extended. * @see org.glassfish.jersey.server.model.ExtendedResource * * @since 2.5.1 */ public boolean isExtended() { return data.extended; } @Override public String toString() { return data.toString(); } @Override public List getComponents() { List components = new LinkedList(); components.addAll(getChildResources()); components.addAll(getResourceMethods()); final ResourceMethod resourceLocator = getResourceLocator(); if (resourceLocator != null) { components.add(resourceLocator); } return components; } }