I am providing an implementation of ParamConverterProvider in a JAX-RS application. This implementation provides gives a definition of the abstract method in the interface with signature as:
public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation annotations[]);
I am playing with an online tutorial and modified the implementation as follows.
package org.koushik.javabrains.rest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Calendar;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import org.koushik.javabrains.rest.MyDate;
#Provider
public class MyDateConverterProvider implements ParamConverterProvider {
#Override
public <MyDate> ParamConverter<MyDate> getConverter(final Class<MyDate> rawType, Type genericType, Annotation[] annotations) {
return new ParamConverter<MyDate>() {
#Override
public MyDate fromString(String value) {
Calendar requestedDate = Calendar.getInstance();
if ("tomorrow".equalsIgnoreCase(value)) {
requestedDate.add(Calendar.DATE, 1);
}
else if ("yesterday".equalsIgnoreCase(value)) {
requestedDate.add(Calendar.DATE, -1);
}
org.koushik.javabrains.rest.MyDate myDate = new org.koushik.javabrains.rest.MyDate();
myDate.setDate(requestedDate.get(Calendar.DATE));
myDate.setMonth(requestedDate.get(Calendar.MONTH));
myDate.setYear(requestedDate.get(Calendar.YEAR));
return rawType.cast(myDate);
}
#Override
public String toString(MyDate myBean) {
if (myBean == null) {
return null;
}
return myBean.toString();
}
};
}
}
I have a few questions:
Why do i need to provide the entire package name when instatiatiating the type T that will be returned from getConverter. It has the same package name as this current class, still I need to write the fully qualified name , else I get a compilation error , cannot instantiate.
org.koushik.javabrains.rest.MyDate myDate = new org.koushik.javabrains.rest.MyDate();. It doesn't make a difference if I import this at the top. import org.koushik.javabrains.rest.MyDate;
At the start of the method I get this warning The type parameter MyDate is hiding the type MyDate.
This provider works fine and am able to cast request path params, but am still wondering how can I avoid the above two.
You don't. You already import the class, therefore you should not require the fully qualified name.
Don't re-parameterize your overridden method. Just remove the type parameter from the method signature. Note its absence in the below snippet.
#Override
public ParamConverter<MyDate> getConverter(final Class<MyDate> rawType, Type genericType, Annotation[] annotations) {
Related
I'm attempting to make a Repository interface, that our business logic can use, with the idea that if we decide to change the data source that backs the repositories, that the business logic would not be affected. We have many clients that would be using this library, so we have begun making a suite of controllers that can be reused among clients. This is the repository interface:
package //redacted
import java.util.Collection;
import java.util.List;
public interface Repository<T extends Object, R extends RepositoryQuery<T>> {
T add(T entity);
Collection<T> add(Collection<T> entities);
void remove(T entity);
Collection<T> getAll();
T get(Integer id) throws InvalidEntryException;
List<T> get(Collection<Integer> ids);
List<T> query(R query);
List<T> query(T query);
}
This is the controller I'm having problems with:
package //redacted
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import /* redacted */.entities.pointbank.PointBankBalance;
import /* redacted */.entities.user.User;
import /* redacted */.queries.PendingPointsBalance;
import /* redacted */.repository.Repository;
import /* redacted */.repository.RepositoryQuery;
public class RetrievePendingPointBalance {
private Repository<PointBankBalance, ? extends RepositoryQuery<PointBankBalance>> repository;
private Constructor<? extends PendingPointsBalance> pendingQuery;
public RetrievePendingPointBalance(Repository<PointBankBalance, ? extends RepositoryQuery<PointBankBalance>> repository,
Constructor<? extends PendingPointsBalance> pendingQuery) {
this.repository = repository;
this.pendingQuery = pendingQuery;
}
public PointBankBalance execute(User user) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
PendingPointsBalance query = pendingQuery.newInstance();
query.setUser(user);
return repository.query(query).get(0);
}
}
I'm trying to create RepositoryQuery, which is what will be responsible for handling more than just CRUD operations. Here is the interface for that:
package //redacted
import java.util.List;
public interface RepositoryQuery<T extends Object> {
List<T> execute();
}
The idea there, is that each query can extend can extend the interface with setters for the parameters it will need. Then each implementation can extend that query interface with specifics of what it will need (example: setJdbcTemplate(...) )
On the controller above though, the line:
return repository.query(query).get(0);
is a compilation error, and I do not know why. Is my approach inherently flawed, or am I just missing something?
Full Error:
[ERROR] /Users/redacted/src/main/java/com/redacted/controllers/RetrievePendingPointBalance.java:[26,28] no suitable method found for query(com.redacted.queries.PendingPointsBalance)
method com.redated.repository.Repository.query(capture#1 of ? extends com.redated.repository.RepositoryQuery<com.redacted.entities.pointbank.PointBankBalance>) is not applicable
(argument mismatch; com.redacted.queries.PendingPointsBalance cannot be converted to capture#1 of ? extends com.redacted.repository.RepositoryQuery<com.redacted.entities.pointbank.PointBankBalance>)
method com.redacted.repository.Repository.query(com.redacted.entities.pointbank.PointBankBalance) is not applicable
(argument mismatch; com.redacted.queries.PendingPointsBalance cannot be converted to com.redacted.entities.pointbank.PointBankBalance)
And my error from Eclipse:
Edit: PointBankBalance interface
package //redacted
import com./* redacted */.entities.pointbank.PointBankBalance;
import com./* redacted */.entities.user.User;
import com./* redacted */.repository.RepositoryQuery;
public interface PendingPointsBalance extends RepositoryQuery<PointBankBalance> {
void setUser(User user);
}
Your problem is that the method takes an R and you defined R as ? extends RepositoryQuery<PointBankBalance> in the repository field declaration. It's uncertain which subclass ? refers to, so the compiler won't let you use a specific one. You could potentially set that field to an instance of Repository<PointBankBalance, AnotherPointsBalance>, in which case PendingPointsBalance wouldn't be a valid argument to its query method.
One way to fix it would be replacing Repository<PointBankBalance, ? extends RepositoryQuery<PointBankBalance>> with Repository<PointBankBalance, PendingPointsBalance>.
Or you could simplify things by removing the R parameter entirely:
public interface Repository<T extends Object> {
...
List<T> query(RepositoryQuery<T> query);
List<T> query(T query);
}
Then just replace Repository<PointBankBalance, ? extends RepositoryQuery<PointBankBalance>> with Repository<PointBankBalance>.
In the following code snippet I'm calling the method doStuff once on an instance of Subclass. However it is intercepted twice.
Note that doStuff was defined in the parent class SuperClass. If doStuff was defined in SubClass the interception logic would work as expected: only one interception.
Am I using Byte Buddy incorrectly?
package com.test;
import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import java.util.concurrent.Callable;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType.Builder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import org.junit.Test;
public class ReproBugTest {
#Test
public void reproBug() {
new AgentBuilder.Default().type(nameStartsWith("com.test"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(
Builder<?> builder,
TypeDescription td) {
return builder.method(any())
.intercept(
MethodDelegation.to(MethodInterceptor.class));
}
})
.installOn(
ByteBuddyAgent.installOnOpenJDK());
SubClass subClass = new SubClass();
subClass.doStuff();
}
}
class SuperClass {
public void doStuff() {
System.out.println("Doing stuff...");
}
}
class SubClass extends SuperClass {
}
class MethodInterceptor {
#RuntimeType
public static Object intercept(#SuperCall Callable<?> zuper)
throws Exception {
// Intercepted twice, bug?
System.out.println("Intercepted");
Object returnValue = zuper.call();
return returnValue;
}
}
You are intercepting the method call for every type, i.e. for both Subclass and SuperClass. You need to further specify your interceptor for what methods to intercept. In you case, you only want to intercept methods if they are declared by a given type.
This is easy to implement. Instead of builder.method(any()), you should intercept builder.method(isDeclaredBy(td)). This way, a method is only intercepted if it is declared by the intercepted type.
Finally, I can see from, your source code that you are using an older version of Byte Buddy. Version 0.7-rc6 runs stable, has additional features and fixes several bugs. (However, some APIs still need to be changed.)
I'm writing a method supposed to retrieve all annotations of a specific method declaring class and its superclasses.
By using the method getAnnotations() on the declaring class, the resulting table contains only the declaring class annotations and the superclass annotations are ignored.
If I remove the annotations of the declaring class, then the superclass annotation are present.
What am I missing here?
The simplified method retrieving the annotations :
public void check(Method invokedMethod) {
for (Annotation annotation : invokedMethod.getDeclaringClass().getAnnotations()) {
// Do something ...
}
}
(All annotations I'm trying the get have the #Inherited annotation)
In case you need to process several annotations of the same type, the standard approach is does not work, because annotations are stored in a Map with annotation types as the key. (See more here). Here is how I would work around this problem (just go through all super classes manually):
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
public class AnnotationReflectionTest {
public static void main(String[] args) throws Exception {
check(Class2.class.getMethod("num", new Class[0]));
}
public static void check(Method invokedMethod) {
Class<?> type = invokedMethod.getDeclaringClass();
while (type != null) {
for (Annotation annotation : type.getDeclaredAnnotations()) {
System.out.println(annotation.toString());
}
type = type.getSuperclass();
}
}
}
#Inherited
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface Annot1 {
int num();
}
#Annot1(num = 5)
class Class1 {
public int num() {
return 1;
}
}
#Inherited
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface Annot2 {
String text();
}
#Annot2(text = "ttt")
class Class2 extends Class1 {
public int num() {
return super.num() + 1;
}
}
What version of Java and what OS do you use?
I had to write a simple method with
private <A extends Annotation> A getAnnotationFromType(Class<?> classType, final Class<A> annotationClass) {
while ( !classType.getName().equals(Object.class.getName()) ) {
if ( classType.isAnnotationPresent(annotationClass)) {
return classType.getAnnotation(annotationClass);
}
classType = classType.getSuperclass();
}
return null;
}
This might be obvious to most people, but if you came here looking for fields of a class and its superclasses, you can use
myClass.getFields()
to get all fields, also of superclasses, instead of
myClass.getDeclaredFields()
which only returns the fields of the class itself. Similarly for methods and constructors.
The API I'm working with has decided to accept UUIDs as Base32 encoded strings, instead of the standard hexadecimal, dash separated format that UUID.fromString() expects. This means that I can't simply write #QueryParam UUID myUuid as a method parameter, as the conversion would fail.
I'm working around this by writing a custom object with a different fromString converter to be used with the Jersey #QueryString and #FormParam annotations. I would like to be able to access the context of the conversion in the fromString method so that I can provide better error messages. Right now, all I can do is the following:
public static Base32UUID fromString(String uuidString) {
final UUID uuid = UUIDUtils.fromBase32(uuidString, false);
if (null == uuid) {
throw new InvalidParametersException(ImmutableList.of("Invalid uuid: " + uuidString));
}
return new Base32UUID(uuid);
}
I would like to be able to expose which parameter had the invalid UUID, so my logged exceptions and returned user errors are crystal clear. Ideally, my conversion method would have an extra parameter for details, like so:
public static Base32UUID fromString(
String uuidString,
String parameterName // New parameter?
) {
final UUID uuid = UUIDUtils.fromBase32(uuidString, false);
if (null == uuid) {
throw new InvalidParametersException(ImmutableList.of("Invalid uuid: " + uuidString
+ " for parameter " + parameterName));
}
return new Base32UUID(uuid);
}
But this would break the by-convention means that Jersey finds a parsing method :
Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String) and java.util.UUID.fromString(String));
I've also looked at the ParamConverterProvider that can also be registered to provide conversion, but it doesn't seem to add enough context either. The closest it provides is the an array of Annotations, but from what I can tell of the annotation, you can't backtrack from there to determine which variable or method the annotation is on. I've found this and this examples, but they don't make effective use of of the Annotations[] parameter or expose any conversion context that I can see.
Is there any way to get this information? Or do I need to fallback to an explicit conversion call in my endpoint method?
If it makes a difference, I'm using Dropwizard 0.8.0, which is using Jersey 2.16 and Jetty 9.2.9.v20150224.
So this can be accomplished with a ParamConverter/ParamConverterProvider. We just need to inject a ResourceInfo. From there we can obtain the resource Method, and just do some reflection. Below is an example implementation that I've tested and works for the most part.
import java.lang.reflect.Type;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.annotation.Annotation;
import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import javax.ws.rs.FormParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.InternalServerErrorException;
#Provider
public class Base32UUIDParamConverter implements ParamConverterProvider {
#Context
private javax.inject.Provider<ResourceInfo> resourceInfo;
private static final Set<Class<? extends Annotation>> ANNOTATIONS;
static {
Set<Class<? extends Annotation>> annots = new HashSet<>();
annots.add(QueryParam.class);
annots.add(FormParam.class);
ANNOTATIONS = Collections.<Class<? extends Annotation>>unmodifiableSet(annots);
}
#Override
public <T> ParamConverter<T> getConverter(Class<T> type,
Type type1,
Annotation[] annots) {
// Check if it is #FormParam or #QueryParam
for (Annotation annotation : annots) {
if (!ANNOTATIONS.contains(annotation.annotationType())) {
return null;
}
}
if (Base32UUID.class == type) {
return new ParamConverter<T>() {
#Override
public T fromString(String value) {
try {
Method method = resourceInfo.get().getResourceMethod();
Parameter[] parameters = method.getParameters();
Parameter actualParam = null;
// Find the actual matching parameter from the method.
for (Parameter param : parameters) {
Annotation[] annotations = param.getAnnotations();
if (matchingAnnotationValues(annotations, annots)) {
actualParam = param;
}
}
// null warning, but assuming my logic is correct,
// null shouldn't be possible. Maybe check anyway :-)
String paramName = actualParam.getName();
System.out.println("Param name : " + paramName);
Base32UUID uuid = new Base32UUID(value, paramName);
return type.cast(uuid);
} catch (Base32UUIDException ex) {
throw new BadRequestException(ex.getMessage());
} catch (Exception ex) {
throw new InternalServerErrorException(ex);
}
}
#Override
public String toString(T t) {
return ((Base32UUID) t).value;
}
};
}
return null;
}
private boolean matchingAnnotationValues(Annotation[] annots1,
Annotation[] annots2) throws Exception {
for (Class<? extends Annotation> annotType : ANNOTATIONS) {
if (isMatch(annots1, annots2, annotType)) {
return true;
}
}
return false;
}
private <T extends Annotation> boolean isMatch(Annotation[] a1,
Annotation[] a2,
Class<T> aType) throws Exception {
T p1 = getParamAnnotation(a1, aType);
T p2 = getParamAnnotation(a2, aType);
if (p1 != null && p2 != null) {
String value1 = (String) p1.annotationType().getMethod("value").invoke(p1);
String value2 = (String) p2.annotationType().getMethod("value").invoke(p2);
if (value1.equals(value2)) {
return true;
}
}
return false;
}
private <T extends Annotation> T getParamAnnotation(Annotation[] annotations,
Class<T> paramType) {
T paramAnnotation = null;
for (Annotation annotation : annotations) {
if (annotation.annotationType() == paramType) {
paramAnnotation = (T) annotation;
break;
}
}
return paramAnnotation;
}
}
Some notes about the implementation
The most important part is how the ResourceInfo is injected. Since this needs to be accessed in a request scope context, I injected with javax.inject.Provider, which allows us to retrieve the object lazily. When we actually do get() it, it will be within a request scope.
The thing to be cautious about is that it get() must be called inside the fromString method of the ParamConverter. The getConverter method of the ParamConverterProvider is called many times during application load, so we cannot try and call the get() during this time.
The java.lang.reflect.Parameter class I used is a Java 8 class, so in order to use this implementation, you need to be working on Java 8. If you are not using Java 8, this post may help in trying to get the parameter name some other way.
Related to the above point, the compiler argument -parameters needs to be applied when compiling, to be able to access the formal parameter name, as pointed out here. I just put it in the maven-cmpiler-plugin as pointed out in the link.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<inherited>true</inherited>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<testCompilerArgument>-parameters</testCompilerArgument>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
If you don't do this, a call to Parameter.getName() will result in argX, X being the index of the parameter.
The implementation only allows for #FormParam and #QueryParam.
One important thing to note (that I learned the hard way), is that all exceptions that aren't handle in the ParamConverter (only applies to #QueryParam in this case), will lead to a 404 with no explanation of the problem. So you you need to make sure you handle your exception if you want a different behavior.
UPDATE
There is a bug in the above implementation:
// Check if it is #FormParam or #QueryParam
for (Annotation annotation : annots) {
if (!ANNOTATIONS.contains(annotation.annotationType())) {
return null;
}
}
The above is called during model validation when getConverter is called for each parameter. The above code only works is there is only one annotation. If there is another annotation aside from #QueryParam or #FormParam, say #NotNull, it will fail. The rest of the code is fine. It does actually work under the assumption that there will be more than one annotation.
The fix to the above code, would be something like
boolean hasParamAnnotation = false;
for (Annotation annotation : annots) {
if (ANNOTATIONS.contains(annotation.annotationType())) {
hasParamAnnotation = true;
break;
}
}
if (!hasParamAnnotation) return null;
Just to expand on peeskillets answer above, you might also consider solving the problem with dropwizard and jerseys built in bean validation. So, instead of throwing an exception from inside the factory method, you'd do this:
public class Base32UUID{
#NotNull
private final UUID uuid;
private Base32UUID(UUID uuid){
this.uuid = uuid;
}
public static Base32UUID fromString(String uuidString) {
final UUID uuid = UUIDUtils.fromBase32(uuidString, false);
return new Base32UUID(uuid);
}
}
In your reousource method, you make sure to annotate the parameter with #Valid, this should already be enough for dropwizard to return a descriptive error message, however if you want to customize the returned value, create and register an exceptionmapper, like so:
public class ValidationMapper implements ExceptionMapper<ConstraintViolationException>{
#Context
UriInfo uri;
#Context
private javax.inject.Provider<ResourceInfo> resourceInfo;
#Override
public Response toResponse(ConstraintViolationException exception) {
return Response.ok().build();
}
}
And in your application class:
environment.jersey().register(ValidationMapper.class);
As you can see, all the required resources peeskillet injected in his paramconverter example, can be injected in the exception mapper. The bean validation approach just seems a little more appropriate to me + once set up, it can be used for validating pretty much any input anywhere in your application, not just null checks, but regular expression matches, emails, number ranges etc, and making sure the application always return an appropriate and appropriately formatted response.
According to the dropwizard docs validation should work out of the box, but I had to add dropwizard-validation and jersey-bean-validation to my pom file to make it work:
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-validation</artifactId>
<version>0.8.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.19</version>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</exclusion>
</exclusions>
</dependency>
Using Spring, I can get all beans of a certain type that are currently defined using this:
#Resource
private List<Foo> allFoos;
How does Spring do this? I thought type information of generics was erased at runtime. So how does Spring know about the type Foo of the list and only inject dependencies of the correct type?
To illustrate: I don't have a bean of type "List" that contains the other beans. Instead, Spring creates that list and adds all beans of the correct type (Foo) to this list and then injects that list.
Not all generic information is lost at runtime:
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
public class Main {
public static List<String> list;
public static void main(String[] args) throws Exception {
Field field = Main.class.getField("list");
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
Type[] types = pType.getActualTypeArguments();
for (Type t : types) {
System.out.println(t);
}
} else {
System.err.println("not parameterized");
}
}
}
Output:
class java.lang.String