org.glassfish.jersey.server.model.RuntimeResource Maven / Gradle / Ivy
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 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.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.glassfish.jersey.uri.PathPattern;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
/**
* Runtime resource is a group of {@link Resource resources} with the same {@link Resource#getPath() path}
* regular expression. Runtime resource is constructed from {@link Resource resources} creating
* the {@link ResourceModel resource model}.
*
* Runtime resource can have child runtime resources which are groups of child resources of all resources constructing this
* runtime resource.
*
* The following example shows how Runtime resource structure is built from Resource model:
*
* @Path("{foo}")
* public class TemplateResourceFoo {
* @GET
* @Path("child")
* public String getFoo() {...}
*
* @Path("{x}")
* @GET
* public String getX() {...}
*
* @Path("{y}")
* @POST
* public String postY(String entity) {...}
* }
*
* @Path("{bar}")
* public class TemplateResourceBar {
* @Path("{z}")
* @PUT
* public String putZ(String entity) {...}
* }
*
*
* Will be represented by RuntimeResources:
*
*
* line
* RuntimeResource regex
* Grouped Resources (paths)
* Parent RuntimeResource (line)
*
*
* 1
* "/([^/]+?)"
* Resource("{foo}"), Resource("{bar}")
* no parent
*
*
* 2
* "child"
* Child Resource("child")
* 1
*
*
* 3
* "/([^/]+?)"
* Child Resource("{x}"), Child Resource("{y}"), Child Resource("{z}")
* 1
*
*
*
* @author Miroslav Fuksa (miroslav.fuksa at oracle.com)
*
*/
public class RuntimeResource implements ResourceModelComponent {
/**
* Runtime Resource builder.
*/
static class Builder {
private final List resourceWrappers;
private final String regex;
private final List childRuntimeResourceBuilders;
/**
* Create new {@link RuntimeResource runtime resource} builder instance.
* @param resourceWrappers List of wrappers of resources with same regex that creates a RuntimeResource.
* @param childRuntimeResourceBuilders List of builders of child runtime resources that belong runtime resource.
* @param regex Path regular expression.
*/
public Builder(List resourceWrappers, List childRuntimeResourceBuilders,
String regex) {
this.childRuntimeResourceBuilders = childRuntimeResourceBuilders;
this.resourceWrappers = resourceWrappers;
this.regex = regex;
}
/**
* Build new RuntimeResource from this builder.
* @param parent Parent runtime resource.
* @return New RuntimeResource instance.
*/
public RuntimeResource build(RuntimeResource parent) {
return new RuntimeResource(resourceWrappers, childRuntimeResourceBuilders, parent, regex);
}
}
/**
* Comparator of RuntimeResources based on rules respecting resource matching algorithm.
*/
public static final Comparator COMPARATOR = new Comparator() {
@Override
public int compare(RuntimeResource o1, RuntimeResource o2) {
return PathPattern.COMPARATOR.compare(o1.getPathPattern(), o2.getPathPattern());
}
};
private final String regex;
private final List resourceMethods;
private final List resourceLocators;
private final List childRuntimeResources;
private final List resourceWrappers;
private final List resources;
private final RuntimeResource parent;
private final PathPattern pathPattern;
private RuntimeResource(List resourceWrappers, List childRuntimeResourceBuilders,
RuntimeResource parent, String regex) {
this.parent = parent;
this.pathPattern = resourceWrappers.get(0).getResource().getPathPattern();
this.resourceWrappers = Lists.newArrayList(resourceWrappers);
this.resources = Lists.transform(resourceWrappers, new Function() {
@Override
public Resource apply(ResourceWrapper input) {
return input.getResource();
}
});
this.regex = regex;
this.resourceMethods = Lists.newArrayList();
this.resourceLocators = Lists.newArrayList();
this.childRuntimeResources = Lists.newArrayList();
for (Builder childRuntimeResourceBuilder : childRuntimeResourceBuilders) {
this.childRuntimeResources.add(childRuntimeResourceBuilder.build(this));
}
Collections.sort(this.childRuntimeResources, COMPARATOR);
for (ResourceWrapper hierarchyAwareResource : resourceWrappers) {
for (ResourceMethod resourceMethod : hierarchyAwareResource.getResource().getResourceMethods()) {
this.resourceMethods.add(resourceMethod);
}
final ResourceMethod resourceLocator = hierarchyAwareResource.getResource().getResourceLocator();
if (resourceLocator != null) {
this.resourceLocators.add(resourceLocator);
}
}
}
/**
* {@link Resource Resource} wrapper that contains link to resource's parent.
*/
static class ResourceWrapper {
private final Resource resource;
private final Resource parentResource;
/**
* Get the parent of the {@link #getResource() resource}.
* @return Non null resource if {@link #getResource() resource} is a child resource, null otherwise.
*/
public Resource getParentResource() {
return parentResource;
}
/**
* Get the resource.
* @return Resource.
*/
public Resource getResource() {
return resource;
}
/**
* Create new instance from resource and it's parent.
* @param parentResource Parent of the {@code resource}. Can be null if the resource is not a child resource.
* @param resource Child or parent resource.
*/
public ResourceWrapper(Resource parentResource, Resource resource) {
this.parentResource = parentResource;
this.resource = resource;
}
}
/**
* Get child runtime resources of this resource.
*
* @return List of child runtime resource.
*/
public List getChildRuntimeResources() {
return childRuntimeResources;
}
/**
* Get regular expression of path pattern of this runtime resource.
*
* @return Matching regular expression.
*/
public String getRegex() {
return regex;
}
/**
* Get resource methods (excluding resource locators) of all {@link Resource resources} of this runtime resource.
*
* @return List of resource methods.
*/
public List getResourceMethods() {
return resourceMethods;
}
/**
* Get resource locators of all {@link Resource resources} of this runtime resource.
*
* Note that valid RuntimeResource should have only one resource locator. This method is used for validation purposes.
*
* @return List of resource locators.
*/
public List getResourceLocators() {
return resourceLocators;
}
/**
* Return the resource locator of this resource.
* @return Resource locator of this runtime resource.
*/
public ResourceMethod getResourceLocator() {
if (resourceLocators.size() >= 1) {
return resourceLocators.get(0);
} else {
return null;
}
}
/**
* Get parent of this runtime resource.
* @return Parent runtime resource if this runtime resource is a child resource, null otherwise.
*/
public RuntimeResource getParent() {
return parent;
}
/**
* Get path pattern for matching purposes.
* @return Path pattern.
*/
public PathPattern getPathPattern() {
return pathPattern;
}
/**
* Get full regular expression of this runtime resource prefixed by regular expression of parent if present.
* @return Full resource regular expression.
*/
public String getFullPathRegex() {
if (parent == null) {
return regex;
} else {
return parent.getRegex() + regex;
}
}
/**
* Return parent {@link Resource resources} of {@link Resource resources} from this runtime resource. The returned list
* is ordered so that the position of the parent resource in the returned list is the same as position of its child resource
* in list returned by {@link #getResources()}. Simply said the order of lists returned
* from {@link #getParentResources()} and {@link #getResources()} from parent-child point of view is the same. If the resource
* has no parent then the element {@code null} is in the list.
*
* @return Parent resource list with resources if this runtime resource is child resource or {@code null} elements if
* this runtime resource is the parent resource.
*/
public List getParentResources() {
return Lists.transform(resourceWrappers, new Function() {
@Override
public Resource apply(ResourceWrapper input) {
return input.getParentResource();
}
});
}
/**
* Return parent {@link Resource resource} of the {@code resource} which is grouped in this runtime resource.
* If a {@code resource} is present in {@link #getResources()} more than one time the parent of this first resource
* will be returned.
*
* @param resource Resource whose parent should be returned.
* @return Parent resource or null if the resource has no parent.
* @throws IllegalArgumentException when resource is not in this RuntimeResource.
*/
public Resource getFirstParentResource(Resource resource) {
for (ResourceWrapper resourceWrapper : resourceWrappers) {
if (resourceWrapper.getResource() == resource) {
return resourceWrapper.getParentResource();
}
}
throw new IllegalArgumentException("RuntimeResource does not contain the resource.");
}
/**
* Get resources creating this runtime resource.
* @return List of resources with same path regular expression which this resource is based on.
*/
public List getResources() {
return resources;
}
@Override
public void accept(ResourceModelVisitor visitor) {
visitor.visitRuntimeResource(this);
}
@Override
public List extends ResourceModelComponent> getComponents() {
return getChildRuntimeResources();
}
}