nicer NPE when null args are specified to ReST methods

This commit is contained in:
Adrian Cole 2012-09-16 11:38:55 -07:00
parent 085bc43d48
commit 85b5006084
2 changed files with 108 additions and 51 deletions

View File

@ -140,6 +140,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
@ -1014,7 +1015,7 @@ public class RestAnnotationProcessor<T> {
Annotation[] annotations = request.getJavaMethod().getParameterAnnotations()[entry.getKey()];
for (Annotation a : annotations) {
if (Nullable.class.isAssignableFrom(a.annotationType()))
if (NULLABLE.apply(a))
continue OUTER;
}
Preconditions.checkNotNull(null, request.getJavaMethod().getName() + " parameter " + (entry.getKey() + 1));
@ -1188,17 +1189,11 @@ public class RestAnnotationProcessor<T> {
for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((PathParam) key).value();
String paramValue;
if (extractors != null && extractors.size() > 0) {
ParamParser extractor = (ParamParser) extractors.iterator().next();
paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
} else {
paramValue = args[entry.getKey()].toString();
}
pathParamValues.put(paramKey, paramValue);
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
if (paramValue.isPresent())
pathParamValues.put(paramKey, paramValue.get().toString());
}
}
if (method.isAnnotationPresent(PathParam.class) && method.isAnnotationPresent(ParamParser.class)) {
String paramKey = method.getAnnotation(PathParam.class).value();
String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
@ -1208,6 +1203,33 @@ public class RestAnnotationProcessor<T> {
return pathParamValues;
}
protected Optional<?> getParamValue(Method method, Object[] args, Set<Annotation> extractors,
Entry<Integer, Set<Annotation>> entry, String paramKey) {
Object arg = args[entry.getKey()];
if (arg == null && containsNullable(method.getParameterAnnotations()[entry.getKey()]))
return Optional.absent();
checkNotNull(arg, "param{%s} for method %s.%s", paramKey, method.getDeclaringClass().getSimpleName(),
method.getName());
if (extractors != null && extractors.size() > 0) {
ParamParser extractor = (ParamParser) extractors.iterator().next();
return Optional.of(injector.getInstance(extractor.value()).apply(arg));
}
return Optional.of(arg);
}
private static final Predicate<Annotation> NULLABLE = new Predicate<Annotation>() {
@Override
public boolean apply(Annotation in) {
return Nullable.class.isAssignableFrom(in.annotationType());
}
};
private static boolean containsNullable(Annotation[] annotations) {
return Iterables.any(ImmutableSet.copyOf(annotations), NULLABLE);
}
private Multimap<String, String> encodeValues(Multimap<String, String> unencoded, char... skips) {
Multimap<String, String> encoded = LinkedHashMultimap.create();
for (Entry<String, String> entry : unencoded.entries()) {
@ -1226,14 +1248,9 @@ public class RestAnnotationProcessor<T> {
for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((MatrixParam) key).value();
String paramValue;
if (extractors != null && extractors.size() > 0) {
ParamParser extractor = (ParamParser) extractors.iterator().next();
paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
} else {
paramValue = args[entry.getKey()].toString();
}
matrixParamValues.put(paramKey, paramValue);
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
if (paramValue.isPresent())
matrixParamValues.put(paramKey, paramValue.get().toString());
}
}
@ -1257,16 +1274,9 @@ public class RestAnnotationProcessor<T> {
for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((FormParam) key).value();
String paramValue;
if (extractors != null && extractors.size() > 0) {
ParamParser extractor = (ParamParser) extractors.iterator().next();
paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
} else {
Object pvo = args[entry.getKey()];
Preconditions.checkNotNull(pvo, paramKey);
paramValue = pvo.toString();
}
formParamValues.put(paramKey, paramValue);
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
if (paramValue.isPresent())
formParamValues.put(paramKey, paramValue.get().toString());
}
}
@ -1289,16 +1299,9 @@ public class RestAnnotationProcessor<T> {
for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((QueryParam) key).value();
Object paramValue;
if (extractors != null && extractors.size() > 0) {
ParamParser extractor = (ParamParser) extractors.iterator().next();
paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
} else {
paramValue = args[entry.getKey()];
}
if (paramValue != null) {
queryParamValues.put(paramKey, paramValue.toString());
}
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
if (paramValue.isPresent())
queryParamValues.put(paramKey, paramValue.get().toString());
}
}
@ -1321,15 +1324,9 @@ public class RestAnnotationProcessor<T> {
for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((PayloadParam) key).value();
Object paramValue;
if (extractors != null && extractors.size() > 0) {
ParamParser extractor = (ParamParser) extractors.iterator().next();
paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
} else {
paramValue = args[entry.getKey()] != null ? args[entry.getKey()] : null;
}
postParams.put(paramKey, paramValue);
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
if (paramValue.isPresent())
postParams.put(paramKey, paramValue.get());
}
}
return postParams;

View File

@ -499,6 +499,12 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
@QueryParams(keys = { "foo", "fooble" }, values = { "bar", "baz" })
public void foo3(@QueryParam("robbie") String robbie) {
}
@FOO
@Path("/")
@QueryParams(keys = { "foo", "fooble" }, values = { "bar", "baz" })
public void foo3Nullable(@Nullable @QueryParam("robbie") String robbie) {
}
}
public void testUnEncodeQuery() {
@ -538,6 +544,25 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertEquals(request.getMethod(), "FOO");
}
@Test
public void testNiceNPEQueryParam() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestQuery.class.getMethod("foo3", String.class);
try {
factory(TestPath.class).createRequest(method, (String) null);
} catch (NullPointerException e) {
assertEquals(e.getMessage(), "param{robbie} for method TestQuery.foo3");
}
}
public void testNoNPEOnQueryParamWithNullable() throws SecurityException, NoSuchMethodException {
Method method = TestQuery.class.getMethod("foo3Nullable", String.class);
HttpRequest request = factory(TestPath.class).createRequest(method, (String) null);
assertEquals(request.getEndpoint().getHost(), "localhost");
assertEquals(request.getEndpoint().getPath(), "/");
assertEquals(request.getEndpoint().getQuery(), "foo=bar&fooble=baz");
assertEquals(request.getMethod(), "FOO");
}
public interface TestPayloadParamVarargs {
@POST
public void varargs(HttpRequestOptions... options);
@ -1398,6 +1423,11 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
public void onePath(@PathParam("path") String path) {
}
@GET
@Path("/{path}")
public void onePathNullable(@Nullable @PathParam("path") String path) {
}
@GET
@Path("/{path1}/{path2}")
public void twoPaths(@PathParam("path1") String path, @PathParam("path2") String path2) {
@ -1436,6 +1466,16 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
}
}
@Test
public void testNiceNPEPathParam() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPath.class.getMethod("onePath", String.class);
try {
factory(TestPath.class).createRequest(method, (String) null);
} catch (NullPointerException e) {
assertEquals(e.getMessage(), "param{path} for method TestPath.onePath");
}
}
@Test
public void testPathParamExtractor() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPath.class.getMethod("onePathParamExtractor", String.class);
@ -1463,6 +1503,16 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertPayloadEquals(request, null, null, false);
}
@Test
public void testNiceNPEMatrixParam() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPath.class.getMethod("oneMatrixParamExtractor", String.class);
try {
factory(TestPath.class).createRequest(method, (String) null);
} catch (NullPointerException e) {
assertEquals(e.getMessage(), "param{one} for method TestPath.oneMatrixParamExtractor");
}
}
@Test
public void testFormParamExtractor() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPath.class.getMethod("oneFormParamExtractor", String.class);
@ -1472,6 +1522,16 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertPayloadEquals(request, "one=l", "application/x-www-form-urlencoded", false);
}
@Test
public void testNiceNPEFormParam() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPath.class.getMethod("oneFormParamExtractor", String.class);
try {
factory(TestPath.class).createRequest(method, (String) null);
} catch (NullPointerException e) {
assertEquals(e.getMessage(), "param{one} for method TestPath.oneFormParamExtractor");
}
}
@Test
public void testParamExtractorMethod() throws SecurityException, NoSuchMethodException {
Method method = TestPath.class.getMethod("onePathParamExtractorMethod", String.class);