Skip to content
JakeHoward edited this page Jul 20, 2015 · 2 revisions

Binding is one of the most important concepts in Utterlyidle. It lets you define how the HTTP world should be mapped to the Java world. E.g. how HTTP GET /message/123 should be bound to MessageResource.displayMessage(int messageId).

Utterlyidle comes with two types of bindings out of the box:

  • annotation bindings
  • DSL bindings

The library also has an injection point where you can define your own binding mechanism e.g. according to your favourite convention.

In this tutorial we'll focus on two default mechanisms that let you define bindings.

Annotation bindings

This style of binding should be familiar to people who have been using JAX-RS. To use annotation bindings you need to define your resources as annotated classes.

public class MyModule implements ResourcesModule {
 
    public Resources addResources(Resources resources) throws Exception {
        return resources.add(annotatedClass(MyResource.class));
    }
}

Or if you're writing a very simple app and prefer to use default ApplicationBuilder over custom modules:

ApplicationBuilder.application().addAnnotated(MyResource.class)

The two code snippets above tell Utterlyidle that it should inspect MyResource class to find the annotations that define our bindings. MyResource class might look like that:

import com.googlecode.utterlyidle.annotations.GET;
import com.googlecode.utterlyidle.annotations.Path;

public class MyResource {
    @GET
    @Path("resourcePath")
    public MyResourceReturnType myResourceMethod() {
        // some logic
    }
}

To make your Java class a resource you need to annotate it with @Path at a class or a method level and additionally you need to provide one of the HTTP method annotations. All annotation bindings live in the com.googlecode.utterlyidle.annotations package. Here's the overview of the available annotations:

HTTP method annotations: @GET, @POST, @PUT, @DELETE, @OPTIONS

import com.googlecode.utterlyidle.ApplicationBuilder;
import com.googlecode.utterlyidle.annotations.*;

import static com.googlecode.utterlyidle.ServerConfiguration.defaultConfiguration;

public class BindingsTest {
    public static void main(String[] args) throws Exception {
        ApplicationBuilder.application().addAnnotated(MyResource.class).start(defaultConfiguration().port(8000));
    }

    @Path("methods")
    public static class MyResource {
        @GET
        public String get() {
            return "get";
        }

        @POST
        public String post() {
            return "post";
        }

        @PUT
        public String put() {
            return "put";
        }

        @DELETE
        public String delete() {
            return "delete";
        }

        @OPTIONS
        public String options() {
            return "options";
        }
    }
}

You can use a command line tool like curl or a firefox plugin poster to test how Utterlyidle handles different HTTP methods.

@Path

@Path annotation represents the part of URL that consists of segments separated by a forward slash, located between the host/port and query parameters. @Path can be specified on a class:

@Path("resource")
public class MyResource {
    @GET
    public String get() {
        return "get";
    }
}

or on an individual method:

public class MyResource {
    @GET
    @Path("resource")
    public String get() {
        return "get";
    }
}

When @Path is attached to a class and a method, the two segments get concatenated.

@Path("class")
public class MyResource {
    @GET
    @Path("method")
    public String get() {
        return "get";
    }
}

The resource path in the example above is /class/method. So if our application is deployed at http://utterlyidle.com/basePath and we send GET request to http://uterrlyidle.com/basePath/class/method, the get() method will be invoked.

Please note that @Path("path") and @Path("/path") are equivalent as the library removes a leading slash.

@QueryParam

Utterlyidle can bind HTTP methods to Java methods based on query parameters.

@Path("foo")
public class GettableWithQuery {
    @GET
    public String get(@QueryParam("name") String name) {
        return name;
    }
}

If a GET URL looks like: http://utterlyidle.com/foo?name=value, then the name query parameter will be passed to a Java method.

@FormParam

@Path("foo")
public class Postable {
    @POST
    public String post(@FormParam("name") String name) {
        return name;
    }
}

If the HTTP POST request is sent with a name parameter it will be mapped to the Java method argument.

POST http://utterlyidle.com/foo HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 10

name=value

When using an HTTP client, please make sure that it sends a Content-Type.

@PathParam

In Utterlyidle we can parametrize segments of the URL.

@Path("foo/{id}")
public class PathParameter {
    @GET
    public String get(@PathParam("id") String id) {
        return id;
    }
}

So when we request http://utterlyidle.com/foo/bar, Uterrlyidle will extract id path parameter from the path. You may be wondering what happens when the path parameter contains a space. What if we request http://utterlyidle.com/foo/bar%20baz or http://utterlyidle.com/foo/bar+baz? They will be translated to "bar test" on the server side when the path parameter is extracted from the URL.

@CookieParam

You can use this annotation to do the binding based on the Cookie header extracted from the HTTP request.

public class GettableWithCookies {
    @GET
    @Path("bar")
    public String get(@CookieParam("name") String name) {
        return name;
    }
}

@QueryParam, @FormParam, @PathParam, @CookieParam - advanced cases

In this section we will cover more advanced cases related to param annotations. All the examples will be based on the @QueryParam, but the same rules apply to @FormParam, @PathParam and @CookieParam.

If we have many query parameters parameters matching, Utterlyidle picks the one with the most parameters.

@Path("foo")
public class MultipleGets {
    @GET
    public String get() {
        return "no parameters";
    }

    @GET
    public String get(@QueryParam("arg") String arg) {
        return arg;
    }

    @GET
    public String get(@QueryParam("arg") String arg, @QueryParam("anotherArg") String anotherArg) {
       return arg+" "+anotherArg;
    }
}
  • http://uterrlyidle.com/foo returns "no parameters"
  • http://uterrlyidle.com/foo?arg=value returns 'value"
  • http://uterrlyidle.com/foo?arg=value&anotherArg=twice returns "value twice"

If you want to explicitly control the priority of resource methods, you can use @Priority. E.g. a method with @Priority(10) is given precedence over a method with @Priority(0). By default @Priority defines three constants Priority.Low (-10), Priority.Medium (0) and Priority.High (10)

You can also specify default values for the query parameters that are not provided in the HTTP request.

@Path("foo")
public class UsesDefaultValue {
    @GET
    public String get(@QueryParam("name") @DefaultValue("Dan") String name) {
        return name;
    }
}

Alternatively, you can use an Option type.

@Path("foo") 
public class GetWithOptionalString {
    @GET
    public String get(@QueryParam("name") Option<String> name) {
        return name.getOrElse("Dan");
    }
}

In the example above we use getOrElse() to access a value of the name query parameter or a default when no query parameter is present.

All the examples we've seen so far were using String query parameters. In Utterlyidle you're free to use any type you want to represent your query parameters. Let's start with a basic example:

@Path("foo")
public class GetWithStrongTypeWithConstructor {
    @GET
    public String get(@QueryParam("name") ClassWithPublicConstructor name) {
        // some logic
    }
}
public class ClassWithPublicConstructor {

    private final String value;

    public ClassWithPublicConstructor(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return value;
    }
}

Our resource takes a custom type and maps it to a query parameter. Utterlyidle will be able to solve the binding problem because we provided a constructor that takes a String as an argument. If we don't have a public constructor that takes a String argument, but we have a static factory method with a String argument, it's fine too. Finally, what if neither a proper constructor nor a static factory method are available? No worries, Uterrlyidle provides a special type of module called ArgumentScopeModule that lets you define how method argument (e.g. query parameter arguments) should be created.

@Consumes

HTTP message is not just a URI and HTTP method. RESTful web framework/library should also understand HTTP headers. @Consumes annotation instructs Utterlyidle to look at the Content-Type header of the POST/PUT request to bind it to the appropriate Java method. Content-Type is a way of telling the server what type of content the client is sending. Let's take a look at an example:

@Path("text")
public class MultiplePutContent {
    @PUT
    @Consumes("text/plain")
    public String putPlain(InputStream input) {
        return "plain";
    }

    @PUT
    @Consumes("text/html")
    public String putHtml(InputStream input) {
        return "html";
    }
}

The first method will be triggered if the HTTP request contains Content-Type: text/plain header. The second method will be triggered if the HTTP request contains Content-Type: text/html header.

One @Consumes annotation can match multiple MIME types:

@Consumes({"text/plain", "text/xml"})

You can specify this annotation at the method level as we've already seen and also at the class level.

@Produces

@Produces annotation instructs Utterlyidle to look at the Accept header of the HTTP request. The Accept header is an indicator to the server application saying what type of content can be handled on the client side.

@Path("text")
public class GetsWithMimeTypes {
    @GET
    @Produces("text/plain")
    public String getPlain() {
        // plain text
    }

    @GET
    @Produces("application/xml")
    public String getXml() {
        // return Xml
    }

    @GET
    @Produces("text/html")
    public String getHtml() {
        // return HTML
    }
}

When the HTTP request contains Accept: text/html header, getHtml() method will be invoked to serve HTML content to the client.

One @Produces annotation can match multiple MIME types:

@Produces({"text/plain", "text/xml"})

If you inspect HTTP headers sent by your browser you'll find out that Accept headers are usually not as simple as in the examples above. They may look like:

Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

What this header says is that multiple MIME types are supported and some of them are preferred over the others. q stands for quality and it's a measure of preference. E.g In the example above application/xml (q=0.9) is preferred over text/plain (q=0.8). Utterlyidle understands complex Accept headers and can choose a MIME type with the highest quality that's supported on the server side.

If for some reason you can't use the Accept header to choose appropriate resource representation and you prefer to use a file extension instead you can do that too:

// in the request scope container
container.addInstance(ConvertExtensionToAcceptHeader.Replacements.class, replacements(pair("csv", "text/csv")))

The code above will enable .csv extension to be interpreted as a request for the text/csv representation of the resource.

DSL bindings

DSL bindings give you the same mechanism as annotion bindings but you can express everything in code using a declarative DSL. One of the biggest advantages of using DSL bindings is that you can take any third party class and expose it as a RESTful resource without touching the original code.

import com.googlecode.utterlyidle.Resources;
import com.googlecode.utterlyidle.modules.Module;
import com.googlecode.utterlyidle.modules.RequestScopedModule;
import com.googlecode.utterlyidle.modules.ResourcesModule;
import com.googlecode.yadic.Container;

import java.util.Properties;

import static com.googlecode.totallylazy.proxy.Call.method;
import static com.googlecode.totallylazy.proxy.Call.on;
import static com.googlecode.utterlyidle.dsl.BindingBuilder.get;
import static com.googlecode.utterlyidle.dsl.BindingBuilder.queryParam;

public class PropertiesModule implements ResourcesModule, RequestScopedModule {
    public Container addPerRequestObjects(Container container) throws Exception {
        return container.addInstance(Properties.class, System.getProperties());
    }

    public Resources addResources(Resources resources) throws Exception {
        resources.add(get("/properties").resource(method(on(Properties.class).getProperty(queryParam(String.class, "name")))).build());
        return resources.add(get("/properties").resource(method(on(Properties.class).stringPropertyNames())).build());
    }
}

In this example we injected system properties to the request scope and then exposed those properties as a RESTful resource. If we run it on localhost with a basepath set to myapp then:

  • http://localhost:8000/myapp/properties will return a set of properties
  • http://localhost:8000/myapp/properties?name=java.runtime.name wil return a value of the java.runtime.name property

HTTP method and path

When creating DSL bindings we start with a definition of the HTTP method and a path.

com.googlecode.utterlyidle.dsl.BindingBuilder defines 4 static methods: get(String path), post(path), put(path) and delete(path).

Binding binding = get("path").resource(method(on(ThirdPartyClass.class).someMethod())).build();
Binding binding = post("path").resource(method(on(ThirdPartyClass.class).someMethod())).build();
Binding binding = put("path").resource(method(on(ThirdPartyClass.class).someMethod())).build();
Binding binding = delete("path").resource(method(on(ThirdPartyClass.class).someMethod())).build();

You can add those bindings either by adding them explicitly in a resource module:

public Module addResources(Resources resources) throws Exception {
    Binding binding = delete("path").resource(method(on(ThirdPartyClass.class).someMethod())).build();
    resources.add(binding);
    return this;
}

Or if using ApplicationBuilder (Uterrlyidle will create a module for you):

Binding binding = delete("path").resource(method(on(ThirdPartyClass.class).someMethod())).build();
ApplicationBuilder.application().add(binding).start();

Query param, Form param, Path param, Cookie param

Utterlyidle uses totallylazy proxies so that you can get autocompletion support when binding HTTP requests to Java methods. In the following examples we expose ThridPartyClass.someMethod(String) and bind query, form, path and cookie parameters to the String argument in a Java method.

Binding binding = get("path").resource(method(on(ThirdPartyClass.class).someMethod(queryParam(String.class, "paramName")))).build();
Binding binding = get("path").resource(method(on(ThirdPartyClass.class).someMethod(formParam(String.class, "paramName")))).build();
Binding binding = get("path").resource(method(on(ThirdPartyClass.class).someMethod(pathParam(String.class, "paramName")))).build();
Binding binding = get("path").resource(method(on(ThirdPartyClass.class).someMethod(cookieParam(String.class, "paramName")))).build();

Consumes

Binding binding = post("path")
                        .consumes("text/plain")
                        .resource(method(on(ThirdPartyClass.class)
                        .someMethod(formParam(String.class, "paramName")))).build();

Produces

Binding binding = get("path")
                        .produces("text/plain")
                        .resource(method(on(ThirdPartyClass.class)
                        .someMethod())).build();