/*
* Copyright 2012 astamuse company,Ltd.
*
* 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 com.astamuse.asta4d.web.dispatch.mapping;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import com.astamuse.asta4d.Context;
import com.astamuse.asta4d.interceptor.base.ExceptionHandler;
import com.astamuse.asta4d.util.IdGenerator;
import com.astamuse.asta4d.web.WebApplicationConfiguration;
import com.astamuse.asta4d.web.dispatch.DispatcherRuleMatcher;
import com.astamuse.asta4d.web.dispatch.HttpMethod;
import com.astamuse.asta4d.web.dispatch.HttpMethod.ExtendHttpMethod;
import com.astamuse.asta4d.web.dispatch.interceptor.RequestHandlerInterceptor;
import com.astamuse.asta4d.web.dispatch.interceptor.RequestHandlerResultHolder;
import com.astamuse.asta4d.web.util.bean.DeclareInstanceAdapter;
import com.astamuse.asta4d.web.util.bean.DeclareInstanceUtil;
/**
*
* @author e-ryu
*
* @param <H>
* rule with handler configurable
* @param <T>
* rule with target decided
*/
public abstract class UrlMappingRuleSet<H, T> {
public final static String ID_VAR_NAME = UrlMappingRuleSet.class.getName() + "-rule-id";
public final static String REMAP_ID_VAR_NAME = UrlMappingRuleSet.class.getName() + "-remap-rule-id";
public final static int DEFAULT_PRIORITY = 0;
protected final static AtomicInteger Sequencer = new AtomicInteger();
protected final static class InterceptorHolder {
String attribute;
RequestHandlerInterceptor interceptor;
public InterceptorHolder(String attribute, RequestHandlerInterceptor interceptor) {
super();
this.attribute = attribute;
this.interceptor = interceptor;
}
}
protected final static class RequestHandlerHolder {
String attribute;
Object handler;
public RequestHandlerHolder(String attribute, Object handler) {
super();
this.attribute = attribute;
this.handler = handler;
}
}
protected static class InterceptorWrapper implements RequestHandlerInterceptor {
final static String InterceptorInstanceCacheKey = InterceptorWrapper.class.getName() + "#InterceptorInstanceCacheKey";
DeclareInstanceAdapter adapter;
String id;
InterceptorWrapper(DeclareInstanceAdapter adapter) {
this.adapter = adapter;
this.id = InterceptorInstanceCacheKey + "##" + IdGenerator.createId();
}
@Override
public void preHandle(UrlMappingRule rule, RequestHandlerResultHolder holder) {
RequestHandlerInterceptor interceptor = (RequestHandlerInterceptor) adapter.asTargetInstance();
Context.getCurrentThreadContext().setData(id, interceptor);
interceptor.preHandle(rule, holder);
}
@Override
public void postHandle(UrlMappingRule rule, RequestHandlerResultHolder holder, ExceptionHandler exceptionHandler) {
// retrieve the instance that actually was executed
RequestHandlerInterceptor interceptor = Context.getCurrentThreadContext().getData(id);
interceptor.postHandle(rule, holder, exceptionHandler);
}
}
protected HttpMethod defaultMethod = HttpMethod.GET;
protected List<InterceptorHolder> interceptorHolderList = new ArrayList<>();
protected List<RequestHandlerHolder> defaultHandlerList = new ArrayList<>();
protected List<UrlMappingRule> ruleList = new ArrayList<>();
protected List<UrlMappingRuleRewriter> userCustomizedRuleRewriterList = new ArrayList<>();
protected DispatcherRuleMatcher defaultRuleMatcher = WebApplicationConfiguration.getWebApplicationConfiguration().getRuleMatcher();
public void setDefaultMethod(HttpMethod defaultMethod) {
this.defaultMethod = defaultMethod;
}
public void addRequestHandlerInterceptor(String attribute, Object... interceptorList) {
RequestHandlerInterceptor interceptor;
Object instance;
for (Object obj : interceptorList) {
instance = DeclareInstanceUtil.createInstance(obj);
if (instance instanceof RequestHandlerInterceptor) {
interceptor = (RequestHandlerInterceptor) instance;
interceptorHolderList.add(new InterceptorHolder(attribute, interceptor));
} else if (instance instanceof DeclareInstanceAdapter) {
interceptor = new InterceptorWrapper((DeclareInstanceAdapter) instance);
interceptorHolderList.add(new InterceptorHolder(attribute, interceptor));
}
}
}
public void addRequestHandlerInterceptor(Object... interceptorList) {
addRequestHandlerInterceptor(null, interceptorList);
}
public void addDefaultRequestHandler(String attribute, Object... handlerList) {
for (Object handler : handlerList) {
defaultHandlerList.add(new RequestHandlerHolder(attribute, handler));
}
}
public void addDefaultRequestHandler(Object... handlerList) {
addDefaultRequestHandler(null, handlerList);
}
public void addRuleRewriter(UrlMappingRuleRewriter ruleRewriter) {
userCustomizedRuleRewriterList.add(ruleRewriter);
}
protected UrlMappingRule searchRuleById(List<UrlMappingRule> list, String id) {
UrlMappingRule result = null;
Object ruleId;
for (UrlMappingRule rule : list) {
ruleId = rule.extraVar(ID_VAR_NAME);
if (ruleId == null) {
continue;
} else if (ruleId.equals(id)) {
result = rule;
break;
}
}
if (result == null) {
throw new IllegalArgumentException("Can not find a rule is identified by given id:" + id);
} else {
return result;
}
}
public List<UrlMappingRule> getArrangedRuleList() {
List<UrlMappingRule> arrangedRuleList = new ArrayList<>(ruleList);
// user customized rewriting at first
for (UrlMappingRule rule : arrangedRuleList) {
for (UrlMappingRuleRewriter rewriter : userCustomizedRuleRewriterList) {
rewriter.rewrite(rule);
}
}
// config remapped rule
Object reMapId;
UrlMappingRule copyFromRule;
for (UrlMappingRule rule : arrangedRuleList) {
reMapId = rule.extraVar(REMAP_ID_VAR_NAME);
if (reMapId == null) {
continue;
}
copyFromRule = searchRuleById(arrangedRuleList, reMapId.toString());
List<String> originalAttrList = rule.getAttributeList();
Map<String, Object> originalVarMap = rule.getExtraVarMap();
int originalPriority = rule.getPriority();
rule.setAttributeList(new ArrayList<>(copyFromRule.getAttributeList()));
rule.setExtraVarMap(new HashMap<>(copyFromRule.getExtraVarMap()));
rule.setPriority(copyFromRule.getPriority());
rule.setHandlerList(new ArrayList<>(copyFromRule.getHandlerList()));
rule.setInterceptorList(new ArrayList<>(copyFromRule.getInterceptorList()));
rule.setResultTransformerList(new ArrayList<>(copyFromRule.getResultTransformerList()));
// add original configurations back
if (originalAttrList != null) {
rule.getAttributeList().addAll(originalAttrList);
}
if (originalVarMap != null) {
rule.getExtraVarMap().putAll(originalVarMap);
}
if (originalPriority != DEFAULT_PRIORITY) {
rule.setPriority(originalPriority);
}
}
// set interceptor
List<RequestHandlerInterceptor> interceptorList;
for (UrlMappingRule rule : arrangedRuleList) {
interceptorList = new ArrayList<>();
for (InterceptorHolder iHolder : interceptorHolderList) {
if (iHolder.attribute == null) {
interceptorList.add(iHolder.interceptor);
} else if (rule.hasAttribute(iHolder.attribute)) {
interceptorList.add(iHolder.interceptor);
}
}
rule.setInterceptorList(interceptorList);
}
// set default request handler
List<Object> requestHandlerList;
for (UrlMappingRule rule : arrangedRuleList) {
requestHandlerList = new ArrayList<>();
for (RequestHandlerHolder handlerHolder : defaultHandlerList) {
Object handler = DeclareInstanceUtil.createInstance(handlerHolder.handler);
if (handlerHolder.attribute == null) {
requestHandlerList.add(handler);
} else if (rule.hasAttribute(handlerHolder.attribute)) {
requestHandlerList.add(handler);
}
}
requestHandlerList.addAll(rule.getHandlerList());
rule.setHandlerList(requestHandlerList);
}
for (UrlMappingRule rule : arrangedRuleList) {
List<UrlMappingRuleRewriter> list = UrlMappingRuleSetHelper.getBeforeSortRuleRewritter(rule);
if (list != null) {
for (UrlMappingRuleRewriter rewriter : list) {
rewriter.rewrite(rule);
}
}
}
// sort by priority
Collections.sort(arrangedRuleList, new Comparator<UrlMappingRule>() {
@Override
public int compare(UrlMappingRule r1, UrlMappingRule r2) {
int pc = r1.getPriority() - r2.getPriority();
if (pc != 0) {
return pc;
} else {
return r1.getSeq() - r2.getSeq();
}
}
});
return arrangedRuleList;
}
public abstract H add(String sourcePath);
public abstract T add(String sourcePath, String targetPath);
public abstract H add(HttpMethod method, String sourcePath);
public abstract T add(HttpMethod method, String sourcePath, String targetPath);
public abstract H add(ExtendHttpMethod extendMethod, String sourcePath);
public abstract T add(ExtendHttpMethod extendMethod, String sourcePath, String targetPath);
}