/*
* Copyright 2008-2009 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 net.hasor.web.invoker;
import net.hasor.core.BindInfo;
import net.hasor.core.Hasor;
import net.hasor.core.utils.StringUtils;
import net.hasor.web.Invoker;
import net.hasor.web.annotation.Async;
import net.hasor.web.annotation.HttpMethod;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
/**
* 线程安全
* @version : 2013-6-5
* @author 赵永春 (zyc@hasor.net)
*/
public class InMappingDef implements InMapping {
private final long index;
private BindInfo<?> targetType;
private String mappingTo;
private String mappingToMatches;
private Map<String, Method> httpMapping;
private Set<Method> asyncMethod;
private AsyncSupported defaultAsync = AsyncSupported.no;
//
public InMappingDef(long index, BindInfo<?> targetType, String mappingTo, List<Method> methodList, boolean force) {
this.index = index;
this.targetType = Hasor.assertIsNotNull(targetType);
String servicePath = Hasor.assertIsNotNull(mappingTo);
if (StringUtils.isBlank(servicePath)) {
throw new NullPointerException("'" + targetType.getBindType() + "' Service path is empty.");
}
if (!servicePath.matches("/.+")) {
throw new IllegalStateException("'" + targetType.getBindType() + "' Service path format error, must be a '/' at the start.");
}
if (targetType.getBindType().getAnnotation(Async.class) != null) {
this.defaultAsync = AsyncSupported.yes;
}
//
this.mappingTo = servicePath;
this.mappingToMatches = wildToRegex(servicePath).replaceAll("\\{\\w{1,}\\}", "([^/]{1,})");
this.httpMapping = new HashMap<String, Method>();
this.asyncMethod = new HashSet<Method>();
//
if (methodList == null || methodList.isEmpty()) {
return;
}
//
for (Method targetMethod : methodList) {
// .HttpMethod
Annotation[] annos = targetMethod.getAnnotations();
if (annos != null) {
for (Annotation anno : annos) {
HttpMethod httpMethodAnno = anno.annotationType().getAnnotation(HttpMethod.class);
if (httpMethodAnno != null) {
String bindMethod = httpMethodAnno.value();
if (!StringUtils.isBlank(bindMethod)) {
this.httpMapping.put(bindMethod.toUpperCase(), targetMethod);
}
}
}
}
if (targetMethod.getName().equals("execute") && !this.httpMapping.containsKey("execute")) {
this.httpMapping.put(HttpMethod.ANY, targetMethod);
}
// .Async
if (targetMethod.getAnnotation(Async.class) != null) {
this.asyncMethod.add(targetMethod);
}
//
if (!this.httpMapping.containsValue(targetMethod) && force) {
this.httpMapping.put(HttpMethod.ANY, targetMethod);
}
}
}
private static String wildToRegex(String wild) {
//'\\', '$', '^', '[', ']', '(', ')', '{', '|', '+', '.'
wild = wild.replace("\\", "\\\\"); // <-- 必须放在前面
wild = wild.replace("$", "\\$");
wild = wild.replace("^", "\\^");
wild = wild.replace("[", "\\[");
wild = wild.replace("]", "\\]");
wild = wild.replace("(", "\\(");
wild = wild.replace(")", "\\)");
wild = wild.replace("|", "\\|");
wild = wild.replace("+", "\\+");
wild = wild.replace(".", "\\.");
//
wild = wild.replace("*", ".*");
wild = wild.replace("?", ".");
return wild;
}
//
//
@Override
public BindInfo<?> getTargetType() {
return this.targetType;
}
/** 获取映射的地址 */
public String getMappingTo() {
return this.mappingTo;
}
public String getMappingToMatches() {
return this.mappingToMatches;
}
public long getIndex() {
return index;
}
public Method[] getMethods() {
return this.httpMapping.values().toArray(new Method[httpMapping.size()]);
}
//
//
/**
* 首先测试路径是否匹配,然后判断Restful实例是否支持这个 请求方法。
* @return 返回测试结果。
*/
public boolean matchingMapping(Invoker invoker) {
String httpMethod = invoker.getHttpRequest().getMethod();
String requestPath = invoker.getRequestPath();
Hasor.assertIsNotNull(requestPath, "requestPath is null.");
if (!requestPath.matches(this.mappingToMatches)) {
return false;
}
for (String m : this.httpMapping.keySet()) {
if (httpMethod.equals(m)) {
return true;
} else if (HttpMethod.ANY.equals(m)) {
return true;
}
}
return false;
}
//
/**
* 调用目标
* @throws Throwable 异常抛出
*/
public final Method findMethod(final Invoker invoker) {
String requestPath = invoker.getRequestPath();
Hasor.assertIsNotNull(requestPath, "requestPath is null.");
if (!requestPath.matches(this.mappingToMatches)) {
return null;
}
//
String httpMethod = invoker.getHttpRequest().getMethod();
Method targetMethod = this.httpMapping.get(httpMethod.trim().toUpperCase());
if (targetMethod == null) {
targetMethod = this.httpMapping.get(HttpMethod.ANY);
}
return targetMethod;
}
public boolean isAsync(Invoker invoker) {
Method targetMethod = this.findMethod(invoker);
if (targetMethod == null) {
return false;
}
AsyncSupported async = this.asyncMethod.contains(targetMethod) ? AsyncSupported.yes : this.defaultAsync;
return async == AsyncSupported.yes;
}
//
@Override
public Object newInstance(Invoker invoker) throws Throwable {
return invoker.getAppContext().getInstance(getTargetType());
}
//
@Override
public String toString() {
return String.format("pattern=%s ,methodSet=%s ,type %s", //
this.mappingTo, StringUtils.join(this.httpMapping.keySet().toArray(), ","), this.getTargetType());
}
}