/*
* Copyright 2007-2009 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.impl.module;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import net.paoding.rose.web.ControllerErrorHandler;
import net.paoding.rose.web.Invocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author zhiliang.wang
*
*/
public class ErrorHandlerDispatcher implements ControllerErrorHandler {
private Log logger = LogFactory.getLog(getClass());
private static final int INVOCATION_INDEX = 0;
private static final int THROWABLE_INDEX = 1;
private ControllerErrorHandler errorHandler;
private List<ErrorHandlerDelegate> delegates = new ArrayList<ErrorHandlerDelegate>(8);
public ErrorHandlerDispatcher(ControllerErrorHandler errorHandler) {
this.errorHandler = errorHandler;
Method[] methods = this.errorHandler.getClass().getMethods();
for (final Method method : methods) {
if (Modifier.isAbstract(method.getModifiers())
|| Modifier.isStatic(method.getModifiers())) {
continue;
}
if (method.getName().equals("onError")) {
final Class<?>[] parameterClasses = method.getParameterTypes();
if (parameterClasses.length == 2
&& parameterClasses[INVOCATION_INDEX] == Invocation.class
&& Throwable.class.isAssignableFrom(parameterClasses[THROWABLE_INDEX])) {
delegates.add(new ErrorHandlerDelegate() {
@Override
public Method getMethod() {
return method;
}
@Override
public Object onError(Invocation inv, Throwable ex) throws Throwable {
Object[] args = new Object[] { inv, ex };
try {
return method
.invoke(ErrorHandlerDispatcher.this.errorHandler, args);
} catch (Throwable e) {
logger.error("error happened when handling error " + ex.getClass()
+ " at " + ErrorHandlerDispatcher.this.toString());
throw e;
}
}
});
}
}
}
Collections.sort(delegates, new Comparator<ErrorHandlerDelegate>() {
@Override
public int compare(ErrorHandlerDelegate o1, ErrorHandlerDelegate o2) {
if (o1.getMethod().getParameterTypes()[THROWABLE_INDEX].isAssignableFrom(o2
.getMethod().getParameterTypes()[THROWABLE_INDEX])) {
return 1;
} else if (o2.getMethod().getParameterTypes()[THROWABLE_INDEX].isAssignableFrom(o1
.getMethod().getParameterTypes()[THROWABLE_INDEX])) {
return -1;
} else {
return o1.getMethod().getParameterTypes()[THROWABLE_INDEX].getName().compareTo(
o2.getMethod().getParameterTypes()[THROWABLE_INDEX].getName());
}
}
});
}
@Override
public Object onError(Invocation inv, Throwable ex) throws Throwable {
for (ErrorHandlerDelegate delegate : delegates) {
if (delegate.getMethod().getParameterTypes()[THROWABLE_INDEX].isAssignableFrom(ex
.getClass())) {
return delegate.onError(inv, ex);
}
}
throw new Error(
"not found errorHandlerMethod for exceptionClass" + ex.getClass().getName(), ex);
}
@Override
public String toString() {
return errorHandler.toString() + delegates.toString();
}
static abstract class ErrorHandlerDelegate {
abstract Method getMethod();
abstract Object onError(Invocation inv, Throwable ex) throws Throwable;
@Override
public String toString() {
return getMethod().getParameterTypes()[THROWABLE_INDEX].getSimpleName();
}
}
}