Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
ninja.RouterImpl Maven / Gradle / Ivy
/**
* Copyright (C) 2012-2016 the original author or authors.
*
* 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 ninja;
import ninja.utils.MethodReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ninja.utils.NinjaProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.google.inject.Injector;
import java.lang.reflect.Method;
public class RouterImpl implements Router {
private final NinjaProperties ninjaProperties;
private final Logger logger = LoggerFactory.getLogger(RouterImpl.class);
private final List allRouteBuilders = new ArrayList<>();
private final Injector injector;
private List routes;
// for fast reverse route lookups
private Map reverseRoutes;
// This regex works for both {myParam} AND {myParam: .*} (with regex)
private final String VARIABLE_PART_PATTERN_WITH_PLACEHOLDER = "\\{(%s)(:\\s([^}]*))?\\}";
@Inject
public RouterImpl(
Injector injector,
NinjaProperties ninjaProperties) {
this.injector = injector;
this.ninjaProperties = ninjaProperties;
}
@Override
public Route getRouteFor(String httpMethod, String uri) {
if (routes == null) {
throw new IllegalStateException(
"Attempt to get route when routes not compiled");
}
for (Route route : routes) {
if (route.matches(httpMethod, uri)) {
return route;
}
}
return null;
}
@Override
public String getReverseRoute(
Class> controllerClass,
String controllerMethodName) {
Optional> parameterMap = Optional.absent();
return getReverseRoute(controllerClass, controllerMethodName, parameterMap);
}
@Override
public String getReverseRoute(Class> controllerClass,
String controllerMethodName,
Object... parameterMap) {
if (parameterMap.length % 2 != 0) {
logger.error("Always provide key (as String) value (as Object) pairs in parameterMap. That means providing e.g. 2, 4, 6... objects.");
return null;
}
Map map = new HashMap<>(parameterMap.length / 2);
for (int i = 0; i < parameterMap.length; i += 2) {
map.put((String) parameterMap[i], parameterMap[i + 1]);
}
return getReverseRoute(controllerClass, controllerMethodName, Optional.of(map));
}
@Override
public String getReverseRoute(Class> controllerClass,
String controllerMethodName,
Map parameterMap) {
Optional> parameterMapOptional
= Optional.fromNullable(parameterMap);
return getReverseRoute(
controllerClass,
controllerMethodName,
parameterMapOptional);
}
@Override
public String getReverseRoute(
Class> controllerClass,
String controllerMethodName,
Optional> parameterMap) {
Optional route
= getRouteForControllerClassAndMethod(
controllerClass,
controllerMethodName);
if (route.isPresent()) {
// The original url. Something like route/user/{id}/{email}/userDashboard/{name: .*}
String urlWithReplacedPlaceholders
= replaceVariablePartsOfUrlWithValuesProvidedByUser(
route.get().getUrl(),
parameterMap);
String finalUrl = addContextPathToUrlIfAvailable(
urlWithReplacedPlaceholders,
ninjaProperties);
return finalUrl;
}
else {
logger.info(
"Could not find any reverse route for the method {} of the Controller class {}",
controllerMethodName, controllerClass.getSimpleName());
return null;
}
}
@Override
public String getReverseRoute(MethodReference controllerMethodRef) {
return getReverseRoute(
controllerMethodRef.getDeclaringClass(),
controllerMethodRef.getMethodName());
}
@Override
public String getReverseRoute(MethodReference controllerMethodRef, Map parameterMap) {
return getReverseRoute(
controllerMethodRef.getDeclaringClass(),
controllerMethodRef.getMethodName(),
parameterMap);
}
@Override
public String getReverseRoute(MethodReference controllerMethodRef, Object ... parameterMap) {
return getReverseRoute(
controllerMethodRef.getDeclaringClass(),
controllerMethodRef.getMethodName(),
parameterMap);
}
@Override
public String getReverseRoute(MethodReference controllerMethodRef, Optional> parameterMap) {
return getReverseRoute(
controllerMethodRef.getDeclaringClass(),
controllerMethodRef.getMethodName(),
parameterMap);
}
@Override
public void compileRoutes() {
if (routes != null) {
throw new IllegalStateException("Routes already compiled");
}
List routesLocal = new ArrayList<>();
for (RouteBuilderImpl routeBuilder : allRouteBuilders) {
routesLocal.add(routeBuilder.buildRoute(injector));
}
this.routes = ImmutableList.copyOf(routesLocal);
// compile reverse routes for O(1) lookups
this.reverseRoutes = new HashMap<>(this.routes.size());
for (Route route : this.routes) {
// its possible this route is a Result instead of a controller method
if (route.getControllerClass() != null) {
MethodReference controllerMethodRef
= new MethodReference(
route.getControllerClass(),
route.getControllerMethod().getName());
if (this.reverseRoutes.containsKey(controllerMethodRef)) {
// the first one wins with reverse routing so we ignore this route
} else {
this.reverseRoutes.put(controllerMethodRef, route);
}
}
}
logRoutes();
}
@Override
public List getRoutes() {
verifyRoutesCompiled();
return routes;
}
@Override
public RouteBuilder GET() {
RouteBuilderImpl routeBuilder = new RouteBuilderImpl().GET();
allRouteBuilders.add(routeBuilder);
return routeBuilder;
}
@Override
public RouteBuilder POST() {
RouteBuilderImpl routeBuilder = new RouteBuilderImpl().POST();
allRouteBuilders.add(routeBuilder);
return routeBuilder;
}
@Override
public RouteBuilder PUT() {
RouteBuilderImpl routeBuilder = new RouteBuilderImpl().PUT();
allRouteBuilders.add(routeBuilder);
return routeBuilder;
}
@Override
public RouteBuilder DELETE() {
RouteBuilderImpl routeBuilder = new RouteBuilderImpl().DELETE();
allRouteBuilders.add(routeBuilder);
return routeBuilder;
}
@Override
public RouteBuilder OPTIONS() {
RouteBuilderImpl routeBuilder = new RouteBuilderImpl().OPTIONS();
allRouteBuilders.add(routeBuilder);
return routeBuilder;
}
@Override
public RouteBuilder HEAD() {
RouteBuilderImpl routeBuilder = new RouteBuilderImpl().HEAD();
allRouteBuilders.add(routeBuilder);
return routeBuilder;
}
@Override
public RouteBuilder METHOD(String method) {
RouteBuilderImpl routeBuilder = new RouteBuilderImpl().METHOD(method);
allRouteBuilders.add(routeBuilder);
return routeBuilder;
}
private void verifyRoutesCompiled() {
if (routes == null) {
throw new IllegalStateException(
"Routes not compiled!");
}
}
private Optional getRouteForControllerClassAndMethod(
Class> controllerClass,
String controllerMethodName) {
verifyRoutesCompiled();
MethodReference reverseRouteKey
= new MethodReference(controllerClass, controllerMethodName);
Route route = this.reverseRoutes.get(reverseRouteKey);
return Optional.fromNullable(route);
}
private String replaceVariablePartsOfUrlWithValuesProvidedByUser(
String routeUrlWithVariableParts,
Optional> parameterMap) {
String urlWithReplacedPlaceholders = routeUrlWithVariableParts;
if (parameterMap.isPresent()) {
Map queryParameterMap = new HashMap<>(parameterMap.get().size());
for (Entry parameterPair : parameterMap.get().entrySet()) {
boolean foundAsPathParameter = false;
StringBuffer stringBuffer = new StringBuffer();
String buffer = String.format(
VARIABLE_PART_PATTERN_WITH_PLACEHOLDER,
parameterPair.getKey());
Pattern PATTERN = Pattern.compile(buffer);
Matcher matcher = PATTERN.matcher(urlWithReplacedPlaceholders);
while (matcher.find()) {
String resultingRegexReplacement = parameterPair.getValue().toString();
matcher.appendReplacement(stringBuffer, resultingRegexReplacement);
foundAsPathParameter = true;
}
matcher.appendTail(stringBuffer);
urlWithReplacedPlaceholders = stringBuffer.toString();
if (!foundAsPathParameter) {
queryParameterMap.put(parameterPair.getKey(), parameterPair.getValue());
}
}
// now prepare the query string for this url if we got some query params
if (queryParameterMap.size() > 0) {
StringBuilder queryParameterStringBuffer = new StringBuilder();
// The uri is now replaced => we now have to add potential query parameters
for (Iterator> iterator = queryParameterMap.entrySet().iterator();
iterator.hasNext();) {
Entry queryParameterEntry = iterator.next();
queryParameterStringBuffer.append(queryParameterEntry.getKey());
queryParameterStringBuffer.append("=");
queryParameterStringBuffer.append(queryParameterEntry.getValue());
if (iterator.hasNext()) {
queryParameterStringBuffer.append("&");
}
}
urlWithReplacedPlaceholders = urlWithReplacedPlaceholders
+ "?"
+ queryParameterStringBuffer.toString();
}
}
return urlWithReplacedPlaceholders;
}
private String addContextPathToUrlIfAvailable(
String routeWithoutContextPath,
NinjaProperties ninjaProperties) {
// contextPath can only be empty. never null.
return ninjaProperties.getContextPath()
+ routeWithoutContextPath;
}
private void logRoutes() {
// determine the width of the columns
int maxMethodLen = 0;
int maxPathLen = 0;
int maxControllerLen = 0;
for (Route route : getRoutes()) {
maxMethodLen = Math.max(maxMethodLen, route.getHttpMethod().length());
maxPathLen = Math.max(maxPathLen, route.getUri().length());
if (route.getControllerClass() != null) {
int controllerLen = route.getControllerClass().getName().length()
+ route.getControllerMethod().getName().length();
maxControllerLen = Math.max(maxControllerLen, controllerLen);
}
}
// log the routing table
int borderLen = 10 + maxMethodLen + maxPathLen + maxControllerLen;
String border = Strings.padEnd("", borderLen, '-');
logger.info(border);
logger.info("Registered routes");
logger.info(border);
for (Route route : getRoutes()) {
if (route.getControllerClass() != null) {
logger.info("{} {} => {}.{}()",
Strings.padEnd(route.getHttpMethod(), maxMethodLen, ' '),
Strings.padEnd(route.getUri(), maxPathLen, ' '),
route.getControllerClass().getName(),
route.getControllerMethod().getName());
} else {
logger.info("{} {}", route.getHttpMethod(), route.getUri());
}
}
}
}