Skip to content

03. Building blocks

Jim Riordan edited this page Aug 10, 2016 · 3 revisions

Utterlyidle consists of several building blocks that help you structure your application:

  • rest application
  • modules
  • resources
  • response handlers
  • request scoped objects
  • application scoped objects

Application

When building web applications with Utterlyidle you start with an instance of a rest application:

import com.googlecode.utterlyidle.BasePath;
import com.googlecode.utterlyidle.RestApplication;

public class MyWebApplication extends RestApplication {
    public MyWebApplication(BasePath basePath) {
        super(basePath);
    }
}

Modules

The application usually consists of several modules:

import com.googlecode.utterlyidle.BasePath;
import com.googlecode.utterlyidle.RestApplication;
import template.feature1.Feature1Module;
import template.feature2.Feature2Module;
import template.feature3.Feature3Module;
import template.shared.SharedModule;

public class MyWebApplication extends RestApplication {
    public MyWebApplication(BasePath basePath) {
        super(basePath);
        add(new Feature1Module());
        add(new Feature2Module());
        add(new Feature3Module());
        add(new SharedModule());
    }
}

Our preference is to have one module per feature rather than one module per application layer. If you have some infrastructure code that's shared across different modules, we recommend that you create shared modules. Each module can store different types of application building blocks:

import com.googlecode.utterlyidle.Resources;
import com.googlecode.utterlyidle.handlers.ResponseHandlers;
import com.googlecode.utterlyidle.modules.*;
import com.googlecode.yadic.Container;

public class Feature1Module implements RequestScopedModule, ApplicationScopedModule, ResourcesModule, ResponseHandlersModule {
    public Container addPerRequestObjects(Container container) throws Exception {
         return container;
    }

    public Resources addResources(Resources resources) throws Exception {
          return resources;
    }

    public ResponseHandlers addResponseHandlers(ResponseHandlers handlers) throws Exception {
        return handlers;
    }

    public Container addPerApplicationObjects(Container container) throws Exception {
        return container;
    }
}

E.g in the code above we see a module that can store request scope objects, application scope objects, RESTful resources, and response handlers. Each of the interfaces that we implement defines a method to inject those artefacts.

Resource

Let's add a resource to our first module:

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

In the code above, we added a resource with binding annotations. Let's take a look at our resource:

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

public class MyResource {

    private MyApplicationScopeObject applicationScopeObject;
    private MyRequestScopeObject requestScopeObject;

    public MyResource(MyApplicationScopeObject applicationScopeObject, MyRequestScopeObject requestScopeObjectObject) {
        this.applicationScopeObject = applicationScopeObject;
        this.requestScopeObject = requestScopeObjectObject;
    }

    @GET
    @Path("/resourcePath")
    public MyResourceReturnType myResourceMethod() {
        return new MyResourceReturnType(requestScopeObject.calculate(), applicationScopeObject.calculate());
    }
}

where MyResourceReturnType looks like that:

public class MyResourceReturnType {
    private String requestScope;
    private String applicationScope;

    public MyResourceReturnType(String requestScope, String applicationScope) {
        this.requestScope = requestScope;
        this.applicationScope = applicationScope;
    }
}

As we can see in the code, our resource needs two collaborators. Let's go back to our module definition to inject those dependencies.

Request and Application scope objects

    public Container addPerRequestObjects(Container container) throws Exception {
        return container.add(MyRequestScopeObject.class);
    }

    public Container addPerApplicationObjects(Container container) throws Exception {
        return container.add(MyApplicationScopeObject.class);
    }

Utterlyidle uses lightweight DI container called Yadic. To learn how Yadic handles DI please read the Yadic wiki. It's a really simple library and you can easily learn it in less than an hour. In this tutorial we'll assume that MyRequestScopeObject and MyApplicationScopeObject have no argument constructors so that we can just add them directly to the container by specifying their class names.

public class MyApplicationScopeObject {
    public String calculate() {
        return "application scope";
    }
}
public class MyRequestScopeObject {
    public String calculate() {
        return "request scope";
    }
}

Ok, let's see what our module code looks so far:

import com.googlecode.utterlyidle.Resources;
import com.googlecode.utterlyidle.handlers.ResponseHandlers;
import com.googlecode.utterlyidle.modules.*;
import com.googlecode.yadic.Container;

import static com.googlecode.utterlyidle.annotations.AnnotatedBindings.annotatedClass;

public class Feature1Module implements RequestScopedModule, ApplicationScopedModule, ResourcesModule, ResponseHandlersModule {
    public Container addPerRequestObjects(Container container) throws Exception {
        return container.add(MyRequestScopeObject.class);
    }

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

    public ResponseHandlers addResponseHandlers(ResponseHandlers handlers) throws Exception {
        return handlers;
    }

    public Container addPerApplicationObjects(Container container) throws Exception {
        return container.add(MyApplicationScopeObject.class);
    }
}

So we've already added a resource that depends on one application scope object and one request scope object. Before we add a response handler let's see what happens if we run the application in its current state. In the MyWebApplication class, we can add a main method and we'll instantiate our application there and then we can run it on a built-in HTTP server.

public class MyWebApplication extends RestApplication {
    public MyWebApplication(BasePath basePath) {
        super(basePath);
        add(new Feature1Module());
        add(new Feature2Module());
        add(new Feature3Module());
        add(new SharedModule());
    }

    public static void main(String[] args) {
        RestApplication myapp = new MyWebApplication(BasePath.basePath("/myapp"));
        application(myapp).start(defaultConfiguration().port(8000));
    }
}

We specified a base path (myapp) and a default port (8000). Earlier we defined a resource path(@Path("/resourcePath")) so the full URL of our resource should be:

http://localhost:8000/myapp/resourcePath

Note: please make sure that there's no other application using this port. If this is the case you can change the 8000 port to any other port that's available.

When we go to the URL in a browser we'll see something like that:

template.feature1.MyResourceReturnType@4dbb9a58

This value is a toString() representation of MyResourceReturnType that we returned from our resource. If we modify this return type like that:

public class MyResourceReturnType {
    public String requestScope;
    public String applicationScope;

    public MyResourceReturnType(String requestScope, String applicationScope) {
        this.requestScope = requestScope;
        this.applicationScope = applicationScope;
    }

    @Override
    public String toString() {
        return requestScope+"  "+applicationScope;
    }
}

and run the application again, we'll see:

application scope request scope

displayed in a browser.

Using toString() works but in a more sophisticated application we may want to render different responses e.g. based on the Accept header from a client. What's more we probably want to use a templating engine to do the job. We definitely don't want to include any logic and create templates in a toString() method.

Response handlers

ResponseHandlers to the rescue. Let's add one to our module:

import com.googlecode.utterlyidle.Resources;
import com.googlecode.utterlyidle.annotations.HttpMethod;
import com.googlecode.utterlyidle.handlers.ResponseHandlers;
import com.googlecode.utterlyidle.modules.*;
import com.googlecode.yadic.Container;

import static com.googlecode.utterlyidle.PathMatcher.path;
import static com.googlecode.utterlyidle.Requests.method;
import static com.googlecode.utterlyidle.annotations.AnnotatedBindings.annotatedClass;
import static com.googlecode.utterlyidle.handlers.RenderingResponseHandler.renderer;

...

public ResponseHandlers addResponseHandlers(ResponseHandlers handlers) throws Exception {
    return handlers.add(method(HttpMethod.GET).and(path("/resourcePath")), renderer(PrintfRenderer.class));
}

What we're saying here is that GET /resourcePath response should be handled by the PrintfRenderer.

import com.googlecode.utterlyidle.Renderer;

public class PrintfRenderer implements Renderer<MyResourceReturnType> {
    public String render(MyResourceReturnType value) throws Exception {
        return String.format("--- %s --- %s ---", value.requestScope, value.applicationScope);
    }
}

Obviously, in a real world application we would use a more sophisticated templating engine e.g. StringTemplate.

When we run our application again we'll get:

--- request scope --- application scope ---

Clone this wiki locally