package io.eguan.vold.rest.util; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import io.eguan.vold.rest.errors.CustomResourceException; import io.eguan.vold.rest.errors.ServerErrorFactory; import io.eguan.vold.rest.resources.AbstractResource; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; import javax.ws.rs.Path; import javax.ws.rs.core.Context; import javax.ws.rs.core.UriInfo; /** * Utility methods for resource path discovery and manipulation. * * @author oodrive * @author pwehrle * */ public final class ResourcePath { private ResourcePath() { throw new AssertionError("Not instantiable"); } /** * Injects {@link UriInfo} into properly typed and {@link Context}-annotated fields of the target resource. * * @param uriInfo * the {@link UriInfo} to inject * @param targetResource * the target {@link AbstractResource} instance * @throws CustomResourceException * if the annotated field is enforcing Java language access control and the annotated field is either * inaccessible or final * @throws NullPointerException * if the specified target resource is <code>null</code> and the annotated field is an instance field * @throws ExceptionInInitializerError * if the initialization provoked by setting the value fails */ public static final void injectUriInfoContext(final UriInfo uriInfo, final AbstractResource targetResource) throws CustomResourceException, NullPointerException, ExceptionInInitializerError { for (final Field currField : targetResource.getClass().getFields()) { if (!currField.isAnnotationPresent(Context.class) || !UriInfo.class.isAssignableFrom(currField.getType()) || Modifier.isFinal(currField.getModifiers())) { continue; } try { currField.set(targetResource, uriInfo); } catch (final IllegalAccessException e) { throw ServerErrorFactory.newInternalErrorException("Internal error", "Context UriInfo injection failed on " + targetResource, e); } } } /** * Extracts the closest instance in the hierarchy of matched resources from the given {@link UriInfo} that can be * cast to the specified ancestor {@link Class}. * * @param uriInfo * the {@link UriInfo} from which to extract the ancestor * @param ancestorClass * the {@link Class} or any of its subclasses or implementations to search * @return a properly cast instance of the requested {@link Class}, <code>null</code> if no match was found * @throws NullPointerException * if either parameter in <code>null</code> */ public static final <T extends AbstractResource> T extractAncestorFromMatchedResources(final UriInfo uriInfo, final Class<T> ancestorClass) throws NullPointerException { // iterates over matched resources and returns first match for (final Object currResource : uriInfo.getMatchedResources()) { if (ancestorClass.isAssignableFrom(currResource.getClass())) { return ancestorClass.cast(currResource); } } return null; } /** * Extracts the part of the URI path referencing a not yet matched sub-resource. * * This method must only be called by sub-resource locators (see <a href='http * ://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-310003.4.1'>JSR-311 r1.1 chapter 3.4.1</a>), as the * implementation relies on certain assertions being met only in this context. * * @param uriInfo * the {@link UriInfo} of the request causing the call of the sub-resource locator * @return the part of the resource path matching the not yet matched sub-resource * @throws NullPointerException * if the argument is <code>null</code> */ public static final String extractPathForNewSubResource(final UriInfo uriInfo) throws NullPointerException { final List<String> matchedUris = uriInfo.getMatchedURIs(); final List<Object> matchedResources = uriInfo.getMatchedResources(); // checks for preconditions characteristic to sub-resource locators assert (matchedUris.size() > 1); assert (matchedUris.size() == matchedResources.size() + 1); return matchedUris.get(0).replace(matchedUris.get(1), ""); } /** * Extracts the path for a sub-resource of a given type from the class containing the sub-resource locator. * * This method searches the methods of the target class for a sub-resource locator annotated with {@link Path} and * returns the defined path value if the return type matches the requested one. It only makes sense in this * particular context, so don't try anything else! * * @param targetClass * the {@link Class} which defines the {@link Path} annotated method returning the requested resource * type * @param expectedResourceType * the {@link Class} defining the resource type (may be an interface or superclass) * @return the relative resource path if a sub-resource locator could be found, <code>null</code> otherwise */ public static String extractPathForSubResourceLocator(final Class<?> targetClass, final Class<?> expectedResourceType) { if (targetClass == null) { return null; } // iterates over class methods and returns the first match on return type and @Path annotation for (final Method currMethod : targetClass.getMethods()) { if (expectedResourceType.isAssignableFrom(currMethod.getReturnType())) { if (!currMethod.isAnnotationPresent(Path.class)) { continue; } return currMethod.getAnnotation(Path.class).value(); } } // recursively calls all implemented interfaces and returns the first match String result = null; for (final Class<?> currInterface : targetClass.getInterfaces()) { result = extractPathForSubResourceLocator(currInterface, expectedResourceType); if (result != null) { return result; } } // finally, recursively calls the super class result = extractPathForSubResourceLocator(targetClass.getSuperclass(), expectedResourceType); return result; } }