package com.bj58.argo.internal;
import com.bj58.argo.ActionResult;
import com.bj58.argo.Argo;
import com.bj58.argo.BeatContext;
import com.bj58.argo.inject.ArgoSystem;
import com.bj58.argo.internal.actionresult.StatusCodeActionResult;
import com.bj58.argo.logs.Logger;
import com.bj58.argo.route.Router;
import com.bj58.argo.servlet.ArgoDispatcher;
import com.bj58.argo.servlet.ArgoRequest;
import com.bj58.argo.utils.OnlyOnceCondition;
import com.google.common.io.Closeables;
import com.google.inject.Key;
import com.google.inject.OutOfScopeException;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.MultipartConfigElement;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* 用于处理Rest请求调度的核心类
*
*/
@Singleton
public class DefaultArgoDispatcher implements ArgoDispatcher {
private final Argo argo;
private final Router router;
private final StatusCodeActionResult statusCodeActionResult;
private final Key<BeatContext> defaultBeatContextKey = Key.get(BeatContext.class, ArgoSystem.class);
private final MultipartConfigElement config;
private final Logger logger;
@Inject
public DefaultArgoDispatcher(Argo argo, Router router, StatusCodeActionResult statusCodeActionResult, MultipartConfigElement config) {
this.argo = argo;
this.router = router;
this.statusCodeActionResult = statusCodeActionResult;
this.config = config;
this.logger = argo.getLogger(this.getClass());
logger.info("constructed.", this.getClass());
}
@Override
public void init() {
}
@Override
public void service(HttpServletRequest request, HttpServletResponse response) {
ArgoRequest argoRequest = new ArgoRequest(request, config);
try {
BeatContext beatContext = bindBeatContext(argoRequest, response);
route(beatContext);
} finally {
Closeables.closeQuietly(argoRequest);
}
}
private BeatContext bindBeatContext(HttpServletRequest request, HttpServletResponse response) {
Context context = new Context(request, response);
localContext.set(context);
BeatContext beat = argo.injector().getInstance(defaultBeatContextKey);
// 增加默认参数到model
beat.getModel().add("__beat", beat);
context.setBeat(beat);
return beat;
}
private void route(BeatContext beat) {
try {
ActionResult result = router.route(beat);
if (ActionResult.NULL == result)
result = statusCodeActionResult.getSc404();
result.render(beat);
} catch (Exception e) {
statusCodeActionResult.render405(beat);
e.printStackTrace();
logger.error(String.format("fail to route. url:%s", beat.getClient().getRelativeUrl()), e);
//TODO: catch any exceptions.
} finally {
localContext.remove();
}
}
public void destroy() {
}
public HttpServletRequest currentRequest() {
return getContext().getRequest();
}
public HttpServletResponse currentResponse() {
return getContext().getResponse();
}
public BeatContext currentBeatContext() {
return getContext().getBeat();
}
private Context getContext() {
Context context = localContext.get();
if (context == null) {
throw new OutOfScopeException("Cannot access scoped object. Either we"
+ " are not currently inside an HTTP Servlet currentRequest, or you may"
+ " have forgotten to apply " + DefaultArgoDispatcher.class.getName()
+ " as a servlet filter for this currentRequest.");
}
return context;
}
final ThreadLocal<Context> localContext = new ThreadLocal<Context>();
private static class Context {
final HttpServletRequest request;
final HttpServletResponse response;
BeatContext beat;
OnlyOnceCondition onlyOnce = OnlyOnceCondition.create("The current beat has been created.");
Context(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
HttpServletRequest getRequest() {
return request;
}
HttpServletResponse getResponse() {
return response;
}
BeatContext getBeat() {
return beat;
}
void setBeat(BeatContext beat) {
onlyOnce.check();
this.beat = beat;
}
}
}