/* * Copyright 2013 eXo Platform SAS * * 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 juzu.impl.bridge.spi.web; import juzu.HttpMethod; import juzu.Resource; import juzu.impl.bridge.Bridge; import juzu.impl.common.MethodHandle; import juzu.impl.common.UriBuilder; import juzu.impl.plugin.controller.ControllerService; import juzu.impl.plugin.router.RouteDescriptor; import juzu.impl.plugin.router.RouterDescriptor; import juzu.impl.plugin.router.RouterService; import juzu.impl.request.ControllerHandler; import juzu.request.RequestParameter; import juzu.impl.router.PathParam; import juzu.impl.router.Route; import juzu.impl.router.RouteMatch; import juzu.impl.router.Router; import juzu.request.Phase; import java.io.Closeable; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */ public class Handler implements Closeable { /** . */ private static final Phase[] GET_PHASES = {Phase.VIEW, Phase.ACTION, Phase.RESOURCE}; /** . */ private static final Phase[] POST_PHASES = {Phase.ACTION, Phase.VIEW, Phase.RESOURCE}; /** . */ private static final Phase[] OTHER_PHASES = {Phase.RESOURCE}; /** . */ final Bridge bridge; /** . */ final Route root; /** . */ final HashMap<MethodHandle, Route> forwardRoutes; /** . */ final HashMap<Route, RouteDescriptor> backwardRoutes; public Handler(Bridge bridge) throws Exception { this.bridge = bridge; // HashMap<MethodHandle, Route> forwardRoutes = new HashMap<MethodHandle, Route>(); HashMap<Route, RouteDescriptor> backwardRoutes = new HashMap<Route, RouteDescriptor>(); // Route root = new Router(); RouterService router = bridge.getApplication().resolveBean(RouterService.class); if (router != null) { RouterDescriptor desc = router.getDescriptor(); if (desc != null) { Map<RouteDescriptor, Route> ret = desc.popupate(root); for (Map.Entry<RouteDescriptor, Route> entry : ret.entrySet()) { forwardRoutes.put(entry.getKey().handle, entry.getValue()); backwardRoutes.put(entry.getValue(), entry.getKey()); } } } // this.forwardRoutes = forwardRoutes; this.backwardRoutes = backwardRoutes; this.root = root; } public RouteDescriptor getMethods(Route route) { return backwardRoutes.get(route); } public Route getRoute(MethodHandle method) { return forwardRoutes.get(method); } public Route getRoot() { return root; } public Bridge getBridge() { return bridge; } public void handle(WebBridge bridge) throws Throwable { // String requestPath = bridge.getRequestContext().getRequestPath(); // Determine first a possible match from the root route from the request path ControllerHandler requestTarget = null; RouteMatch requestMatch = null; Map<String, RequestParameter> requestParameters = Collections.emptyMap(); if (requestPath.startsWith(bridge.getRequestContext().getPath())) { // HttpMethod requestMethod = bridge.getHttpContext().getMethod(); Iterator<RouteMatch> matches = root.matcher(requestPath.substring(bridge.getRequestContext().getPath().length()), Collections.<String, String[]>emptyMap()); // Determine a method while (matches.hasNext()) { RouteMatch match = matches.next(); RouteDescriptor routeDesc = getMethods(match.getRoute()); if (routeDesc != null) { ControllerHandler target = this.bridge.getApplication().resolveBean(ControllerService.class).getDescriptor().getMethodByHandle(routeDesc.handle); if (target.getPhase() == Phase.VIEW) { if (requestMethod == HttpMethod.POST) { requestTarget = target; requestMatch = match; } else if (requestMethod == HttpMethod.GET) { requestTarget = target; requestMatch = match; break; } } else if (target.getPhase() == Phase.ACTION) { if (requestMethod == HttpMethod.GET) { requestTarget = target; requestMatch = match; } else if (requestMethod == HttpMethod.POST) { requestTarget = target; requestMatch = match; break; } } else if (target.getPhase() == Phase.RESOURCE) { if (Arrays.asList(target.getMethod().getAnnotation(Resource.class).method()).contains(requestMethod)) { requestTarget = target; requestMatch = match; break; } } } } // Determine parameters for the match if (requestMatch != null && (requestMatch.getMatched().size() > 0 || bridge.getRequestContext().getParameters().size() > 0)) { requestParameters = new HashMap<String, RequestParameter>(); for (RequestParameter requestParameter : bridge.getRequestContext().getParameters().values()) { requestParameters.put(requestParameter.getName(), requestParameter); } for (Map.Entry<PathParam, String> entry : requestMatch.getMatched().entrySet()) { RequestParameter requestParameter = RequestParameter.create(entry.getKey().getName(), entry.getValue()); requestParameters.put(requestParameter.getName(), requestParameter); } } } // No method means we either send a server resource // or we look for the handler method if (requestTarget == null) { // If we have an handler we locate the index method requestTarget = this.bridge.getApplication().resolveBean(ControllerService.class).getResolver().resolve(Phase.VIEW, Collections.<String>emptySet()); } // No method -> not found if (requestTarget == null) { bridge.getRequestContext().setStatus(404); } else { if (requestMatch == null) { Route requestRoute = getRoute(requestTarget.getHandle()); if (requestRoute != null) { requestMatch = requestRoute.matches(Collections.<String, String>emptyMap()); if (requestMatch != null) { StringBuilder sb = new StringBuilder(); requestMatch.render(new UriBuilder(sb)); if (!sb.toString().equals(requestPath)) { StringBuilder redirect = new StringBuilder(); bridge.renderRequestURL(redirect); redirect.append(sb); bridge.getRequestContext().sendRedirect(redirect.toString()); return; } } } } // WebRequestBridge requestBridge; if (requestTarget.getPhase() == Phase.ACTION) { requestBridge = new WebActionBridge(this.bridge, this, bridge, requestTarget, requestParameters); } else if (requestTarget.getPhase() == Phase.VIEW) { requestBridge = new WebViewBridge(this.bridge, this, bridge, requestTarget, requestParameters); } else if (requestTarget.getPhase() == Phase.RESOURCE) { requestBridge = new WebResourceBridge(this.bridge, this, bridge, requestTarget, requestParameters); } else { throw new Exception("Cannot decode phase"); } // requestBridge.invoke(); // if (requestBridge.send()) { // ok } else { throw new UnsupportedOperationException("Not yet handled by " + requestBridge.getClass().getSimpleName() + ": " + requestBridge.response); } } } public void close() throws IOException { } }