Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Created December 16, 2015 01:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomasdarimont/eb1bc1d24ccf3decbdc6 to your computer and use it in GitHub Desktop.
Save thomasdarimont/eb1bc1d24ccf3decbdc6 to your computer and use it in GitHub Desktop.
Define method execution order via Method references.
package de.tdlabs.test;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.TreeMap;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@ScenarioTest
public class WebSecurityScenarioTest {
public Steps steps() {
Steps steps = new Steps();
steps.add(this::visitPageRequiringAuthorizationWhileNotLoggedIn);
steps.add(this::login);
steps.add(this::visitSecondPageRequiringAuthorizationWhileLoggedIn);
steps.add(this::logout);
return steps;
}
@Step
void visitPageRequiringAuthorizationWhileNotLoggedIn() {
// attempt to visit page which requires that a user is logged in
// assert user is redirected to login page
}
@Step
void visitSecondPageRequiringAuthorizationWhileLoggedIn() {
// visit another page which requires that a user is logged in
// assert user can access page
}
@Step
void logout() {
// visit logout URL
// assert user has been logged out
}
@Step
void login() {
// submit login form with valid credentials
// assert user is redirected back to previous page requiring authorization
}
public static void main(String[] args) {
new WebSecurityScenarioTest().steps().print();
}
}
class Steps {
Map<Integer, Method> methodMap = new TreeMap<>();
void add(SerializableRunnable step) {
registerStep(methodMap.size(), step);
}
public void print() {
for (Map.Entry<Integer, Method> entry : methodMap.entrySet()) {
System.out.printf("%s %s%n", entry.getKey(), entry.getValue());
}
}
private void registerStep(Integer key, Object step) {
try {
SerializedLambda lambda = tryConvertToSerializedLambda(step);
ClassReader cr = new ClassReader(lambda.getImplClass());
MethodReferenceAnalyzingClassVisitor classVisitor = new MethodReferenceAnalyzingClassVisitor(lambda);
cr.accept(classVisitor, 0);
methodMap.put(key, classVisitor.getActualTargetMethod());
} catch (Exception ex) {
ex.printStackTrace();
}
}
private <T> SerializedLambda tryConvertToSerializedLambda(Object sup) throws Exception {
Method writeReplaceMethod = sup.getClass().getDeclaredMethod("writeReplace");
writeReplaceMethod.setAccessible(true);
Object replacement = writeReplaceMethod.invoke(sup);
if (!(replacement instanceof SerializedLambda)) {
throw new RuntimeException();
}
return (SerializedLambda) replacement;
}
}
@interface ScenarioTest {
}
@interface Step {
}
interface SerializableRunnable extends Runnable, Serializable {}
class MethodReferenceAnalyzingClassVisitor extends ClassVisitor implements Opcodes {
private final LambdaMethodExtractingMethodVisitor methodVisitor;
private SerializedLambda lambda;
public MethodReferenceAnalyzingClassVisitor(SerializedLambda lambda) {
super(ASM5);
this.methodVisitor = new LambdaMethodExtractingMethodVisitor(ASM5);
this.lambda = lambda;
}
public Method getActualTargetMethod() {
return methodVisitor.getMethod();
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (!name.equals(lambda.getImplMethodName())) {
return null;
}
return methodVisitor;
}
}
class LambdaMethodExtractingMethodVisitor extends MethodVisitor implements Opcodes {
private Method method;
public LambdaMethodExtractingMethodVisitor(int api) {
super(api);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
try {
Class<?> targetMethodOwner = Class.forName(owner.replace('/', '.'), false, getClass().getClassLoader());
Class<?>[] paramTypes = convertDescToParameterTypes(desc);
this.method = targetMethodOwner.getDeclaredMethod(name, paramTypes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Class<?>[] convertDescToParameterTypes(String desc) throws ClassNotFoundException {
Type[] argumentTypes = Type.getArgumentTypes(desc);
if (argumentTypes.length == 0) {
return new Class[0];
}
Class<?>[] argumentClasses = new Class[argumentTypes.length];
for (int i = 0; i < argumentClasses.length; i++) {
argumentClasses[i] = Class.forName(argumentTypes[i].getClassName(), false, getClass().getClassLoader());
}
return argumentClasses;
}
public Method getMethod() {
return method;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment