/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package jobs;
import com.google.common.collect.Lists;
import controllers.util.FlashException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.annotation.AnnotationUtils;
import play.Play;
import play.classloading.ApplicationClasses;
import play.classloading.enhancers.Enhancer;
import play.exceptions.ActionNotFoundException;
import play.jobs.Job;
import play.jobs.OnApplicationStart;
import play.mvc.ActionInvoker;
import play.mvc.Controller;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
/**
* Verifies FlashException redirects to ensure they don't point to an invalid action.
*/
@OnApplicationStart
public class VerifyExceptionAnnotations extends Job {
public void doJob() throws NotFoundException {
for (ApplicationClasses.ApplicationClass klass : Play.classes.all()) {
if (Controller.class.isAssignableFrom(klass.javaClass)) {
Class controller = klass.javaClass;
for (Method method : controller.getMethods()) {
if (method.isAnnotationPresent(FlashException.class)) {
FlashException handler = AnnotationUtils.findAnnotation(method, FlashException.class);
assertActionIsValid(controller, method, handler);
}
}
}
}
}
private void assertActionIsValid(Class controller, Method method, FlashException handler) throws NotFoundException {
if (!handler.verify()) {
return;
}
String[] actionList = (StringUtils.isEmpty(handler.value())) ? handler.referrer() : new String[] { handler.value() };
for (String action : actionList) {
// if action is blank we redirect to the referrer, so there's nothing to verify
if (!action.equals("")) {
if (!action.contains(".")) {
action = controller.getName() + "." + action;
}
try {
ActionInvoker.getActionMethod(action);
} catch (ActionNotFoundException e) {
UnsupportedOperationException ex = new UnsupportedOperationException("Action method " + action
+ " couldn't be resolved");
List<StackTraceElement> stackTrace = Lists.newArrayList();
stackTrace.add(createStackTraceElement(controller, method));
Collections.addAll(stackTrace, e.getStackTrace());
ex.setStackTrace(stackTrace.toArray(new StackTraceElement[stackTrace.size()]));
throw ex;
}
}
}
}
/**
* Use javassist to create a StackTraceElement with line numbers from a class + method.
*/
private StackTraceElement createStackTraceElement(Class controller, Method method) throws NotFoundException {
ClassPool pool = Enhancer.newClassPool();
CtClass cls = pool.get(controller.getCanonicalName());
return new StackTraceElement(
controller.getCanonicalName(),
method.getName(),
cls.getClassFile2().getSourceFile(),
cls.getDeclaredMethod(method.getName()).getMethodInfo2().getLineNumber(0) - 1);
}
}