Some content

@WebParamResolver

Note This is just a first quick doc for the @WebParamResolver. We will edit it to be more formal, but this should give you a good first understanding of the power of it.

One of the simplest and more powerful of benefits of Snow is the way it binds HTTP request to Java methods, and especially the way it allows developers to hook their own argument parsers, per data type or annotation.

In short, as we @Web***Handler, developer can annotate any method in a Guice managed object, with @WebParamResolver and Snow will call this method with the request before calling any @Web***Handler method that have this parameter type or annotation.

The best way to understand the pattern is to look at the examples, and luckily, the internal default Snow param resolvers are just simple implementations of this parttern, and can be found in the SystemParamResolvers class.

For example, the SystemParamResolvers resolveLong is as simple as:

SystemWebParamResolvers.resolveLong

@WebParamResolver
public Long resolveLong(AnnotationMap annotationMap, Class paramType, RequestContext rc) {
    WebParam webParam = annotationMap.get(WebParam.class);
    String id = rc.getParam(webParam.value());
    return ObjectUtil.getValue(id, Long.class, null);
}

Note The methods annotated with @WebParamResolver have a fix signature, contrary to the Web***Handler, and must be with the three arguments resolveLong(AnnotationMap annotationMap, Class paramType, RequestContext rc). From RequestContext, developers can get anything the need from HttpSerlvetRequest, HttpSerlvetResponse, to convenient methods to get parameters and cookies.

So, with this definition, Snow will register the type Long with this WebParamResolver, and call it to resolve the param when any Web***Handler methods request such a type. So, a Web***Handler method can be as simple as:

@WebModelHandler(startsWith="/profile")
public Long profilePage(@WebParam("userId")Long userId) {
  // userId will 123 assuming the url was /profile?userId=123
}

In this param resolver, we use the Snow RequestContext instance to get the param value, from the value @WebParam annotation of the argument to get the param name, and use a convenient Snow ObjectUtil to parse the value or return null if failed or not found.

For your application, you can either override these default ones (not really recommended, but application resolvers will take presendence), or make your own custom type. For example, for the https://github.com/BriteSnow/sampleContacts sample application, we did a simple @WebParamResolver for the contact entity as

AppWebParamResolvers

@Singleton
public class AppWebParamResolvers {

    @Inject
    private ContactDao contactDao;
    
    @WebParamResolver
    public Contact resolveContact(AnnotationMap annotationMap, Class paramType, RequestContext rc) {
        WebParam webParam = annotationMap.get(WebParam.class);
        String paramName = webParam.value();
        Long contactId = rc.getParamAs(paramName, Long.class,null);
        return contactDao.get(contactId);
    }
}  

And that's it, with this 4 lines of code, you could use it in any WebHandler as:

ContactPagesWebHandlers

//...  
@WebModelHandler(startsWith="/contact")
public void pageProfile(@WebModel Map m, @WebParam("id")Contact contact){
    m.put("contact", contact);
}
//...  

You can also, add an annotatedWith to further scope your WebParamResolver by annotation rather than just type. For example, the default system WebParamResolver for @WebPath is as follow:

SystemWebParamResolvers.resolveWebPath

public Object resolveWebpath(AnnotationMap annotationMap, Class paramType, RequestContext rc){
    WebPath webPath = annotationMap.get(WebPath.class);
    Object value;
    //if the index has been set, then, return the single Path and convert to the appropriate type.
    if (webPath.value() > -1) {
        value = rc.getResourcePathAt(webPath.value(), paramType, null);
    }
    //otherwise, return the full path
    else {
        value = rc.getResourcePath();
    }        
    return value;
}  

And only this, will allow to have the following in any Web***Handler methods

//...  
@WebModelHandler(startsWith="/profile")
public void pageProfile(@WebModel Map m, @WebPath(1)Long contactId){
    // userId will 123 assuming the url was /profile/123
}
//...