/*
* 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;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.astamuse.asta4d.Context;
import com.astamuse.asta4d.interceptor.base.ExceptionHandler;
import com.astamuse.asta4d.interceptor.base.Executor;
import com.astamuse.asta4d.interceptor.base.GenericInterceptor;
import com.astamuse.asta4d.interceptor.base.InterceptorUtil;
import com.astamuse.asta4d.web.dispatch.interceptor.RequestHandlerInterceptor;
import com.astamuse.asta4d.web.dispatch.interceptor.RequestHandlerResultHolder;
import com.astamuse.asta4d.web.dispatch.mapping.UrlMappingRule;
import com.astamuse.asta4d.web.dispatch.request.RequestHandler;
import com.astamuse.asta4d.web.dispatch.request.ResultTransformer;
import com.astamuse.asta4d.web.dispatch.request.ResultTransformerUtil;
import com.astamuse.asta4d.web.dispatch.response.provider.ContentProvider;
import com.astamuse.asta4d.web.util.bean.AnnotationMethodHelper;
import com.astamuse.asta4d.web.util.bean.DeclareInstanceUtil;
public class DefaultRequestHandlerInvoker implements RequestHandlerInvoker {
public static final String TRACE_VAR_CURRENT_HANDLER = "TRACE_VAR_CURRENT_HANDLER#" + DefaultRequestHandlerInvoker.class;
/* (non-Javadoc)
* @see com.astamuse.asta4d.web.dispatch.RequestHandlerInvoker#invoke(com.astamuse.asta4d.web.dispatch.mapping.UrlMappingRule)
*/
@Override
public List<ContentProvider> invoke(UrlMappingRule rule) throws Exception {
RequestHandlerInvokeExecutor executor = new RequestHandlerInvokeExecutor(rule.getHandlerList(), rule.getResultTransformerList());
RequestHandlerResultHolder holder = new RequestHandlerResultHolder();
InterceptorUtil.executeWithInterceptors(holder, buildInterceptorList(rule), executor);
return holder.getContentProviderList();
}
/*
private static WebPageView getForwardPageView(Map<Class<? extends ForwardDescriptor>, String> forwardDescriptors,
ForwardDescriptor forwardDescriptor) {
String path = forwardDescriptors.get(forwardDescriptor.getClass());
if (StringUtils.isEmpty(path)) {
return null;
}
return new WebPageView(path);
}
*/
private List<RequestHandlerInterceptorWrapper> buildInterceptorList(UrlMappingRule rule) {
List<RequestHandlerInterceptorWrapper> list = new ArrayList<>();
for (RequestHandlerInterceptor interceptor : rule.getInterceptorList()) {
list.add(new RequestHandlerInterceptorWrapper(rule, interceptor));
}
return list;
}
private static class RequestHandlerInterceptorWrapper implements GenericInterceptor<RequestHandlerResultHolder> {
private final UrlMappingRule rule;
private final RequestHandlerInterceptor interceptor;
public RequestHandlerInterceptorWrapper(UrlMappingRule rule, RequestHandlerInterceptor interceptor) {
this.rule = rule;
this.interceptor = interceptor;
}
@Override
public boolean beforeProcess(RequestHandlerResultHolder holder) throws Exception {
interceptor.preHandle(rule, holder);
return holder.getContentProviderList() == null;
}
@Override
public void afterProcess(RequestHandlerResultHolder holder, ExceptionHandler exceptionHandler) {
interceptor.postHandle(rule, holder, exceptionHandler);
}
}
private static class RequestHandlerInvokeExecutor implements Executor<RequestHandlerResultHolder> {
private final List<Object> requestHandlerList;
private final List<ResultTransformer> resultTransformerList;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public RequestHandlerInvokeExecutor(List<Object> requestHandlerList, List<ResultTransformer> resultTransformerList) {
this.requestHandlerList = requestHandlerList;
this.resultTransformerList = resultTransformerList;
}
@Override
public void execute(RequestHandlerResultHolder holder) throws Exception {
List<ContentProvider> cpList = new ArrayList<>();
Object result;
ContentProvider cp;
Context context = Context.getCurrentThreadContext();
for (Object handler : requestHandlerList) {
try {
context.setData(TRACE_VAR_CURRENT_HANDLER, handler);
result = invokeMethodForAnnotation(handler, RequestHandler.class);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
result = t;
} finally {
context.setData(TRACE_VAR_CURRENT_HANDLER, null);
}
if (result != null) {
cp = ResultTransformerUtil.transform(result, resultTransformerList);
cpList.add(cp);
if (!cp.isContinuable()) {
break;
}
}// result != null
}// for
if (cpList.isEmpty()) {
cpList.add(ResultTransformerUtil.transform(null, resultTransformerList));
}
holder.setContentProviderList(cpList);
}
private Object invokeMethodForAnnotation(Object obj, Class<? extends Annotation> annotation) throws Exception {
Object targetObj = DeclareInstanceUtil.retrieveInovkeTargetObject(obj);
Method m = AnnotationMethodHelper.findMethod(targetObj, annotation);
if (m == null) {
// TODO maybe we can return a null?
String msg = String.format("Method not found for annotation %s at class %s:", annotation.toString(), targetObj.getClass()
.getName());
throw new InvocationTargetException(new RuntimeException(msg));
}
try {
return DeclareInstanceUtil.invokeMethod(targetObj, m);
} catch (Exception e) {
String msg = "Error occured when invoke method for annotiona %s on %s";
msg = String.format(msg, annotation.getName(), targetObj.getClass().getName());
logger.error(msg, e);
throw e;
}
}
}
}