Hi guys, sorry for the long email...
This is the feedback that I have got and features we would love to see
in JAX-RS 2.
All those changes are backward compatible as I see them working
currently with VRaptor. The code samples are from VRaptor real
interfaces (and thus, implementations)...
1. (not a request, just showing an example) This is the final code I
would like to see supported while *maintaining compatibility* (unless
its decided not to). To do that, one can simply implement the
following structures (2~):
@Resource
public class SoftwareController {
public Software show(long id) {
Software retrieved = dao.search(id);
return retrieved;
}
}
2. Allows custom resource lookup:
Extract the process of resource lookup and expose it. This allows
anyone to provide its own UrlToResourceTranslator. The default is
JAX-RS1 compatible.
Other simple implementations are the ones where the verb GET is
assumed by default, where getX means the get verb and so on. Every
convention (as with the annotation approach)
is a different implementation. Implementations can be combined through
composition allowing backward *and* forward compatibility:
public interface UrlToResourceTranslator {
ResourceMethod translate(RequestInfo info)
throws ResourceNotFoundException, MethodNotAllowedException;
}
3. Support to a non-default parameter access convention using Java's
debug information. This can be non-default so its up to the user to
decide to use it. I surely prefer to be default:
@Resource
public class SoftwareController {
@Get @Path("/software/{id}")
public Software show(long id) {
Software retrieved = dao.search(id);
return retrieved;
}
}
It can be achieved with backward compatibility by providing the interfaces:
public interface ParameterNameProvider {
String[] parameterNamesFor(AccessibleObject methodOrConstructor);
}
public interface ParametersProvider {
Object[] getParametersFor(ResourceMethod method, List<Message>
errors, ResourceBundle bundle); // the last two parameters are error
related
}
This also allows someone to do this, *if he thinks that will boost his
productivity*:
@Resource
public class SoftwareController {
@Get @Path("/software/{id}")
public Software show(Software software) {
return retrieved; // yep, it was already loaded from the database!
}
}
It also allows one to support file upload easily:
@Resource
public class SoftwareController {
@Get @Path("/softwares")
public void upMyBoy(File orAnyOtherRelatedInterfaceOrClass) {
}
}
4. Also support breaking the view layer from the control layer
If a method does not serialize or return anything, assume a default
behavior so you don't have to repeat yourself all over the place. For
example, if the show method did not
return anything it would go to "/software/show.jsp" or
/software/show.json.jsp or /software/show.xml.jsp etc according to
conneg. To implement it, the semantic model needs to be exposed again:
public interface PathResolver {
String pathFor(ResourceMethod method);
}
5. URI Building
This is one that bothers me a lot. We should be writing OO code, not
xml-oriented, not annotation-oriented, not string-oriented. So why so
many annotations, strings and just one object? This is the
refactor-friendly version of URI builder that we use in VRaptor so
people can refactor code and things are still working:
@Resource
public class UserController {
// inject dao
public User show(long id) {
return dao.load(id);
}
}
@Resource
public class AdminController {
private final Result result;
private final UserDao dao;
// easy to mock, inject => easy to test
public AdminController(Result result, UserDao dao) {
this.result = result;
this.dao = dao;
}
public void create(User created) {
User created = dao.save(prototype);
// refactor friendly + no URI hell
result.redirectTo(UserController.class).show(created);
}
}
Of course, if there is a @Path annotation on top of the method, the
framework knows how to understand that. To do so we provide, again,
everything configurable (and default versions):
public interface PathResolver {
String pathFor(ResourceMethod method);
}
public interface Proxifier {
<T> T proxify(Class<T> type, MethodInvocation<? super T> handler);
}
This is the one that allows hypermedia resource generation in a
refactor-friendly, non-annotation based way, i.e.:
public class Basket implements HypermediaResource {
public void configureRelations(RelationBuilder builder) {
builder.relation("self").uses(BasketsController.class).show(id);
builder.relation("payment").uses(PaymentsController.class).create(id, null);
}
}
6. Allow custom resource creation, so injection can be done in any way
the client wants. Not only Java EE support in all JAX-RS
implementations (done by the user, of course, unless we want to
implement them also):
public interface Container {
<T> T instanceFor(Class<T> type);
<T> boolean canProvide(Class<T> type);
}
7. The heart of a new request. The semantic model for the HTTP<->Resource layer:
public interface Router {
void add(Route route);
ResourceMethod parse(String uri, HttpMethod method, MutableRequest request)
throws ResourceNotFoundException, MethodNotAllowedException,
IllegalStateException;
<T> String urlFor(Class<T> type, Method method, Object... params);
List<Route> allRoutes();
}
And a route:
public interface Route {
boolean canHandle(String uri);
EnumSet<HttpMethod> allowedMethods();
String urlFor(Class<?> type, Method m, Object... params);
boolean canHandle(Class<?> type, Method method);
int getPriority();
String getOriginalUri();
}
8. Allow ordered interceptors. Most of the time behavior can be
composed by using interceptors (as in servlet filters), without the
need for global variables (threadlocals). Interceptor order
might also be important:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Stereotype
public @interface Intercepts {
Class<? extends Interceptor>[] before() default ExecuteMethodInterceptor.class;
Class<? extends Interceptor>[] after() default ResourceLookupInterceptor.class;
}
If a request lifecycle is handled by interfaced interceptors, *any*
behavior can be changed without breaking compatibility.
Validation, for instance, can be supported through an interceptor in
one of several ways: based on the JSR, hibernate validations, hamcrest
and so on
Other examples of interceptors is the typical file download support:
public InputStream download(Music m) {
return manager.load(m).getStream();
}
Regards
Guilherme Silveira
Caelum | Ensino e Inovação
http://www.caelum.com.br/