Skip to content

Instantly share code, notes, and snippets.

@agentgt
Created September 9, 2012 18:07
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 agentgt/3686157 to your computer and use it in GitHub Desktop.
Save agentgt/3686157 to your computer and use it in GitHub Desktop.
Aspect for validating JSR 305 annoations during runtime.
package com.snaphop.aop;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.CheckForNull;
import javax.annotation.concurrent.Immutable;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.ConstructorSignature;
import org.aspectj.lang.reflect.MethodSignature;
public aspect JSR305Aspect {
pointcut immutableConstruction() : call( public (@Immutable *).new(..)) && ! checkForNullConstructionOnClass() ;
pointcut checkForNullConstructionOnClass() : call( public (@CheckForNull *).new(..)) && ! checkForNullConstructionMethod();
pointcut checkForNullConstructionMethod() : execution( @CheckForNull public *.new(..));
pointcut checkForNullMethodOnClass() : call( public (@CheckForNull *) * (..)) && ! checkForNullMethod();
pointcut checkForNullMethod() : execution( @CheckForNull * * (..));
before() : immutableConstruction() || checkForNullConstructionOnClass() {
ConstructorSignature cs = (ConstructorSignature) thisJoinPoint.getSignature();
Constructor<?> c = cs.getConstructor();
Object[] args = thisJoinPoint.getArgs();
Annotation[][] aas = c.getParameterAnnotations();
Class<?> thisClass = thisJoinPoint.getThis().getClass();
checkForNull(cs, c.getParameterTypes(), args, aas, thisClass);
}
before() : checkForNullMethod() || checkForNullMethodOnClass() {
MethodSignature cs = (MethodSignature) thisJoinPoint.getSignature();
Method m = cs.getMethod();
Object[] args = thisJoinPoint.getArgs();
Annotation[][] aas = m.getParameterAnnotations();
Class<?> thisClass = thisJoinPoint.getThis().getClass();
checkForNull(cs, m.getParameterTypes(), args, aas, thisClass);
}
private void checkForNull(Signature cs,
Class<?>[] parameterTypes,
Object[] args,
Annotation[][] aas,
Class<?> thisClass) {
ParametersAreNonnullByDefault nd = thisClass.getAnnotation(ParametersAreNonnullByDefault.class);
if (nd == null) {
nd = thisClass.getPackage().getAnnotation(ParametersAreNonnullByDefault.class);
}
boolean defaultNonNull = nd != null;
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg == null) {
Class<?> type = parameterTypes[i];
boolean applied = false;
Annotation[] as = aas[i];
for (int j = 0; j < as.length && !applied; j++) {
Annotation a = as[j];
if (Nullable.class.equals(a.annotationType())) {
applied = true;
}
if (Nonnull.class.equals(a.annotationType())) {
throw new IllegalArgumentException(errorString(i, type, cs));
}
}
if (defaultNonNull && ! applied) {
throw new IllegalArgumentException(errorString(i, type, cs));
}
}
}
}
private String errorString(int i, Class<?> type, Signature cs) {
return "Null not allowed for index: "
+ i
+ " of type: "
+ type
+ " to method: "
+ cs.toLongString();
}
}
package com.snaphop.aop;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class JSR305Test {
@Before
public void setUp() throws Exception {}
@Test(expected=IllegalArgumentException.class)
public void testConstruction() {
Stuff s = new Stuff(null);
assertNotNull(s);
}
@Test(expected=IllegalArgumentException.class)
//@Test
public void testMethod() {
Stuff s = new Stuff("blah");
s.crapola(null);
}
}
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>1.3.9</version>
</dependency>
package com.snaphop.aop;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
@Immutable
public class Stuff {
private final String name;
//Validation will happen on the caller side
//ie the validation will not occur or happen for serializers
//like Jackson.
//We also don't want validation to happen inside the constructor (even though its sort of not)
//because the object is marked @Immutable semantics.
// this is a "call" pointcut.
public Stuff(@Nonnull String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
//Validation will happen pseudo inside this method ie "execution" pointcut.
@CheckForNull
public void crapola(@Nonnull String stuff) {
return;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment