/*** * Copyright (c) 2009 Caelum - www.caelum.com.br/opensource - guilherme.silveira@caelum.com.br * All rights reserved. * * 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. */ package br.com.caelum.vraptor.restfulie.controller; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.EnumSet; import java.util.List; import br.com.caelum.vraptor.InterceptionException; import br.com.caelum.vraptor.core.InterceptorStack; import br.com.caelum.vraptor.core.RequestInfo; import br.com.caelum.vraptor.core.Routes; import br.com.caelum.vraptor.interceptor.Interceptor; import br.com.caelum.vraptor.ioc.RequestScoped; import br.com.caelum.vraptor.resource.HttpMethod; import br.com.caelum.vraptor.resource.ResourceMethod; import br.com.caelum.vraptor.restfulie.Restfulie; import br.com.caelum.vraptor.restfulie.hypermedia.HypermediaResource; import br.com.caelum.vraptor.restfulie.hypermedia.Transition; import br.com.caelum.vraptor.restfulie.relation.Relation; import br.com.caelum.vraptor.restfulie.relation.RelationBuilder; import br.com.caelum.vraptor.view.Status; /** * Intercepts invocations to state control's intercepted controllers. * * @author guilherme silveira * @author pedro mariano */ @RequestScoped public class ResourceControllerInterceptor<T extends HypermediaResource> implements Interceptor { private final ResourceControl<T> control; private final List<Class<?>> controllers; private final Status status; private final Restfulie restfulie; private final Routes routes; private final RequestInfo info; private final ParameterizedTypeSearcher searcher = new ParameterizedTypeSearcher(); public ResourceControllerInterceptor(ResourceControl<T> control, Restfulie restfulie, Status status, RequestInfo info, Routes routes) { this.control = control; this.restfulie = restfulie; this.status = status; this.info = info; this.routes = routes; this.controllers = Arrays.asList(control.getControllers()); } public boolean accepts(ResourceMethod method) { return controllers.contains(method.getResource().getType()) && method.getMethod().isAnnotationPresent(Transition.class); } public void intercept(InterceptorStack stack, ResourceMethod method, Object instance) throws InterceptionException { ParameterizedType type = searcher.search(control.getClass()); if(analyzeImplementation(method,type)) { stack.next(method, instance); } } private boolean analyzeImplementation(ResourceMethod method, ParameterizedType parameterized) { Type parameterType = parameterized.getActualTypeArguments()[0]; Class<?> found = (Class<?>) parameterType; T resource = retrieveResource(found); if(resource==null) { status.notFound(); return false; } if(allows(resource, method.getMethod())) { return true; } status.methodNotAllowed(allowedMethods()); return false; } private EnumSet<HttpMethod> allowedMethods() { EnumSet<HttpMethod> allowed = routes.allowedMethodsFor(info.getRequestedUri()); allowed.remove(HttpMethod.of(info.getRequest())); return allowed; } private T retrieveResource(Class<?> found) { String parameterName = lowerFirstChar(found.getSimpleName()) + ".id"; String id = info.getRequest().getParameter(parameterName); return control.retrieve(id); } private boolean allows(T resource, Method method) { RelationBuilder builder = restfulie.newRelationBuilder(); resource.configureRelations(builder); for (Relation relation : builder.getRelations()) { if(relation.matches(method)) { return true; } } return false; } private static String lowerFirstChar(String simpleName) { if(simpleName.length()==1) { return simpleName.toLowerCase(); } return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1); } }