/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * 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.alibaba.citrus.async.pipeline.valve; import static com.alibaba.citrus.service.requestcontext.util.RequestContextUtil.*; import static com.alibaba.citrus.springext.util.SpringExtUtil.*; import static com.alibaba.citrus.util.StringUtil.*; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.alibaba.citrus.service.pipeline.Pipeline; import com.alibaba.citrus.service.pipeline.PipelineContext; import com.alibaba.citrus.service.pipeline.Valve; import com.alibaba.citrus.service.pipeline.impl.PipelineImpl; import com.alibaba.citrus.service.pipeline.support.AbstractValveDefinitionParser; import com.alibaba.citrus.service.requestcontext.RequestContext; import com.alibaba.citrus.service.requestcontext.RequestContextChainingService; import com.alibaba.citrus.turbine.pipeline.valve.AbstractInputOutputValve; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.core.task.AsyncTaskExecutor; import org.w3c.dom.Element; /** * 假如screen返回一个<code>Runnable</code>对象,则在另一个线程中执行之,当前valve立即返回,当前线程继续执行并很快退出。 * * @author Michael Zhou */ public class PerformRunnableAsyncValve extends AbstractInputOutputValve { final static String ASYNC_CALLBACK_KEY = "_async_callback_"; private final static Logger log = LoggerFactory.getLogger(PerformRunnableAsyncValve.class); private long defaultTimeout = 0L; private long defaultCancelingTimeout = 1000L; @Autowired private RequestContextChainingService rccs; @Autowired private HttpServletRequest request; @Autowired private AsyncTaskExecutor executor; private Pipeline asyncPipeline; public long getDefaultTimeout() { return defaultTimeout; } public void setDefaultTimeout(long defaultTimeout) { this.defaultTimeout = defaultTimeout; } public long getDefaultCancelingTimeout() { return defaultCancelingTimeout; } public void setDefaultCancelingTimeout(long defaultCancelingTimeout) { this.defaultCancelingTimeout = defaultCancelingTimeout; } public AsyncTaskExecutor getExecutor() { return executor; } public void setExecutor(AsyncTaskExecutor executor) { this.executor = executor; } public Pipeline getAsyncPipeline() { return asyncPipeline; } public void setAsyncPipeline(Pipeline asyncPipeline) { this.asyncPipeline = asyncPipeline; } @Override protected String getDefaultOutputKey() { return ASYNC_CALLBACK_KEY; } @Override protected boolean filterInputValue(Object inputValue) { return inputValue instanceof Callable<?> || inputValue instanceof Runnable; } @Override protected void init() throws Exception { if (asyncPipeline == null) { DoPerformRunnableValve valve = new DoPerformRunnableValve(); valve.afterPropertiesSet(); PipelineImpl pipeline = new PipelineImpl(); pipeline.setValves(new Valve[] { valve }); pipeline.afterPropertiesSet(); asyncPipeline = pipeline; } } public void invoke(final PipelineContext pipelineContext) throws Exception { Object resultObject = consumeInputValue(pipelineContext); if (resultObject == null) { return; } RequestContext rc = getRequestContext(this.request); final HttpServletRequest request = rc.getRequest(); HttpServletResponse response = rc.getResponse(); final AsyncContext asyncContext = request.startAsync(request, response); final AsyncCallbackAdapter callback = new AsyncCallbackAdapter(resultObject, asyncContext, getDefaultTimeout(), getDefaultCancelingTimeout()); setOutputValue(pipelineContext, callback); asyncContext.setTimeout(callback.getTimeout()); final CountDownLatch signal = new CountDownLatch(1); // 执行子pipeline,子pipeline中必须包含DoPerformRunnableValve。 // 执行前将当前的request/response绑定到新线程中。 final Future<?> future = executor.submit(new Callable<Object>() { public Object call() { try { try { rccs.bind(request); asyncPipeline.newInvocation(pipelineContext).invoke(); } finally { rccs.unbind(request); } } catch (Throwable e) { log.error("[" + Thread.currentThread().getName() + "] Exception occurred while doing async task", e); } finally { try { asyncContext.complete(); } catch (IllegalStateException e) { // ignore - 有可能因为超时,该异步请求已经被complete了,再次complete将会抛异常。 } signal.countDown(); } return null; } }); // 当timeout时中断异步线程,并结束请求 asyncContext.addListener(new AsyncListener() { public void onComplete(AsyncEvent event) throws IOException { } public void onTimeout(AsyncEvent event) throws IOException { log.debug("Async task timed out."); future.cancel(true); try { if (signal.await(callback.getCancelingTimeout(), TimeUnit.MILLISECONDS)) { log.debug("Async task was cancelled"); } else { log.debug("Async task is still running. Tried to complete the task."); // 通知异步线程结束,过了一定时间以后,如果还没有结束,就强制complete。 try { event.getAsyncContext().complete(); } catch (IllegalStateException e) { // ignore - 有可能因为超时,该异步请求已经被complete了,再次complete将会抛异常。 } } } catch (InterruptedException e) { } } public void onError(AsyncEvent event) throws IOException { } public void onStartAsync(AsyncEvent event) throws IOException { } }); pipelineContext.invokeNext(); } public static class DefinitionParser extends AbstractValveDefinitionParser<PerformRunnableAsyncValve> { @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { attributesToProperties(element, builder, "input", "defaultTimeout", "defaultCancelingTimeout"); // sub pipeline Object asyncPipeline = parsePipeline(element, null, parserContext, null, true); if (asyncPipeline != null) { builder.addPropertyValue("asyncPipeline", asyncPipeline); } // executor String executorRef = trimToNull(element.getAttribute("executor-ref")); if (executorRef != null) { builder.addPropertyValue("executor", new RuntimeBeanReference(executorRef)); } } } }