/*
* Copyright 2007-2012 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.paoding.rose.web.portal.impl;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import net.paoding.rose.web.ControllerInterceptorAdapter;
import net.paoding.rose.web.Invocation;
import net.paoding.rose.web.portal.Portal;
import net.paoding.rose.web.portal.PortalUtils;
import net.paoding.rose.web.portal.Window;
import net.paoding.rose.web.portal.WindowListener;
/**
* 这个拦截器只拦截 Portal 控制器方法,用于等待所有该 portal 的窗口执行完成或进行超时取消。
*
* @author 王志亮 [qieqie.wang@gmail.com]
*
*/
public class PortalWaitInterceptor extends ControllerInterceptorAdapter {
// 只拦截含有 Portal 的控制器方法
@Override
protected boolean isForAction(Method actionMethod, Class<?> controllerClazz) {
for (Class<?> paramType : actionMethod.getParameterTypes()) {
if (paramType == Portal.class) {
return true;
}
}
return false;
}
@Override
public Object after(Invocation inv, Object instruction) throws Exception {
PortalImpl portal = (PortalImpl) PortalUtils.getPortal(inv);
boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
logger.debug(portal + " is going to wait windows.");
}
WindowListener listener = portal;
long deadline;
long begin = System.currentTimeMillis();
if (portal.getTimeout() > 0) {
deadline = begin + portal.getTimeout();
if (debugEnabled) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
logger.debug(portal + ".maxWait=" + portal.getTimeout() + "; deadline="
+ sdf.format(new Date(deadline)));
}
} else {
deadline = 0;
if (debugEnabled) {
logger.debug(portal + ".maxWait=(forever)");
}
}
int winSize = portal.getWindows().size();
int winIndex = 0;
List<Window> windows = portal.getWindows();
for (Window window : windows) {
winIndex++;
Future<?> future = window.getFuture();
if (future.isDone()) {
if (debugEnabled) {
if (future.isCancelled()) {
logger.debug("[" + winIndex + "/" + winSize + "] continue[cancelled]: "
+ window.getName());
}
if (future.isDone()) {
logger.debug("[" + winIndex + "/" + winSize + "] continue[done]: "
+ window.getName());
}
}
continue;
}
long awaitTime = 0;
try {
long begineWait = System.currentTimeMillis();
if (deadline > 0) {
awaitTime = deadline - begineWait;
if (awaitTime > 0) {
if (debugEnabled) {
logger.debug("[" + winIndex + "/" + winSize + "] waiting[begin]: "
+ window.getName() + "; maxWait=" + awaitTime);
}
future.get(awaitTime, TimeUnit.MILLISECONDS);
if (debugEnabled) {
logger.debug("[" + winIndex + "/" + winSize + "] waiting[done]: "
+ window.getName() + "; actualWait="
+ (System.currentTimeMillis() - begineWait));
}
} else {
logger.error("[" + winIndex + "/" + winSize
+ "] waiting[been timeout now]: " + window.getName());
listener.onWindowTimeout(window);
future.cancel(true);
}
} else {
if (debugEnabled) {
logger.debug("[" + winIndex + "/" + winSize + "] waiting[begin]: "
+ window.getName() + "; maxWait=(forever)");
}
future.get();
if (debugEnabled) {
logger.debug("[" + winIndex + "/" + winSize + "] waiting[done]: "
+ window.getName() + "; actualWait="
+ (System.currentTimeMillis() - begineWait));
}
}
} catch (InterruptedException e) {
logger.error("x[" + winIndex + "/" + winSize + "] waiting[interrupted]: "
+ window.getName());
} catch (ExecutionException e) {
logger.error("x[" + winIndex + "/" + winSize + "] waiting[error]: "
+ window.getName(), e);
window.setThrowable(e);
listener.onWindowError(window);
} catch (TimeoutException e) {
logger.error("x[" + winIndex + "/" + winSize + "] waiting[timeout]: "
+ window.getName(), e);
listener.onWindowTimeout(window);
future.cancel(true);
}
}
if (debugEnabled) {
logger.debug("[" + winIndex + "/" + winSize + "] size of simple windows = " + winIndex);
}
//
if (logger.isDebugEnabled()) {
logger.debug(portal + ".waitForWindows is done; cost="
+ (System.currentTimeMillis() - begin));
}
return instruction;
}
}