Skip to content
Jim Riordan edited this page Nov 9, 2016 · 3 revisions

Most nontrivial Utterlyidle applications are built of many modules. We recommend creating different modules for different vertical slices of your application (divided by business capabilities) as opposed to horizontal slices (divided by technical layers).

GOOD:

import com.googlecode.utterlyidle.Application;
import com.googlecode.utterlyidle.BasePath;
import com.googlecode.utterlyidle.RestApplication;
import customer.CustomerModule;
import order.OrderModule;

public class ShoppingApplication extends RestApplication {
    public ShoppingApplication(BasePath basePath) {
        super(basePath);
        add(new CustomerModule());
        add(new OrderModule());
    }

    public static void main(String[] args) {
        Application application = new ShoppingApplication(BasePath.basePath("/foo"));
    }
} }
}

BAD:

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

public class ShoppingApplication extends RestApplication {
    public ShoppingApplication(BasePath basePath) {
        super(basePath);
        add(new UIModule());
        add(new DomainModule());
        add(new PersistenceModule());
    }

    public static void main(String[] args) {
        Application application = new ShoppingApplication(BasePath.basePath("/foo"));
    }
} }
}

ResourcesModule

To define RESTful resources we use ResourceModule that provides addResources method.

package customer;

import com.googlecode.utterlyidle.Resources;
import com.googlecode.utterlyidle.modules.Module;
import com.googlecode.utterlyidle.modules.ResourcesModule;

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

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

RequestScopedModule

If a resource depends on an object that needs to be created on each request we need to use a RequestScopedModule to provide this per request collaborator.

package customer;

public class CustomerResource {

    private CustomerService service;

    public CustomerResource(CustomerService service) {
        this.service = service;
    }
    
    // resource methods
}
package customer;

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 static com.googlecode.utterlyidle.annotations.AnnotatedBindings.annotatedClass;

public class CustomerModule implements ResourcesModule, RequestScopedModule {
    public Resources addResources(Resources resources) throws Exception {
        return resources.add(annotatedClass(CustomerResource.class));
    }

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

ApplicationScopedModule

Some objects should be created only once per application. Some examples would be a database storage, scheduler, properties, date provider etc. ApplicationScopedModule defines addPerApplicationObjects method where you can inject those object that will be created only once in the application lifecycle.

package customer;

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

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

public class CustomerModule implements ResourcesModule, RequestScopedModule, ApplicationScopedModule {
    public Resources addResources(Resources resources) throws Exception {
        return resources.add(annotatedClass(CustomerResource.class));
    }

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

    public Container addPerApplicationObjects(Container container) throws Exception {
        return container.add(CustomerStorage.class, DBCustomerStorage.class);
    }
}

ArgumentScopedModule

You can use this module if you want to create objects that will be used in the resource method arguments.

package customer;

import com.googlecode.utterlyidle.Response;
import com.googlecode.utterlyidle.annotations.POST;


public class CustomerResource {

    private CustomerService service;

    public CustomerResource(CustomerService service) {
        this.service = service;
    }

    @POST
    public Response create(Customer customer) {
        Customer createdCustomer = service.create(customer);
        return null;
    }

    // resource methods
}

In the example above we want to pass a customer object instead of several different String values matching form parameters.

package customer;

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

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

public class CustomerModule implements ResourcesModule, RequestScopedModule, ApplicationScopedModule, ArgumentScopedModule {
    public Resources addResources(Resources resources) throws Exception {
        return resources.add(annotatedClass(CustomerResource.class));
    }

    public Container addPerArgumentObjects(Container container) throws Exception {
        return container.addActivator(Customer.class, CustomerActivator.class);
    }


    // other methods
}

ArgumentScopedModule provides injection point where we can define how the Customer object should be created.

package customer;

import com.googlecode.utterlyidle.FormParameters;

import java.util.concurrent.Callable;

public class CustomerActivator implements Callable<Customer> {

    private FormParameters formParameters;

    public CustomerActivator(FormParameters formParameters) {
        this.formParameters = formParameters;
    }

    public Customer call() throws Exception {
        // convert form parameters to customer object
    }
}

In this example we use an activator that gets FormParameters injected into its constructor and converts form parameters to Customer object.

ResponseHandlersModule

https://github.com/bodar/utterlyidle/wiki/09.-Rendering

StartupModule

It's a common use case that some operation should be performed when the application starts. E.g. you may have a scheduler that should run on the application startup without any trigger from the client. StartupModule defines a start method where you can specify what action should be performed after the application starts.

public class JobsModule implements ResourcesModule, ApplicationScopedModule, RequestScopedModule, StartupModule {
    public Resources addResources(Resources resources) throws Exception {
        return resources.add(annotatedClass(BatchJobsResource.class));
    }


    public Container start(Container container){
        BatchJobsResource batchJobsResource = container.get(BatchJobsResource.class);
        batchJobsResource.start();
        return container;
    }

    // more code 
}

Another common use case for a startup module may be a sending a warmup request:

import com.googlecode.totallylazy.Callable1;
import com.googlecode.utterlyidle.HttpHandler;
import com.googlecode.utterlyidle.RequestBuilder;
import com.googlecode.utterlyidle.Response;
import com.googlecode.utterlyidle.modules.StartupModule;
import com.googlecode.yadic.Container;

import static com.googlecode.totallylazy.Exceptions.ignoringException;

public class LessWarmupModule implements StartupModule {
    @Override
    public Container start(Container requestScope) {
        final HttpHandler handler = requestScope.get(HttpHandler.class);
        ignoringException(requestLessStyle()).apply(handler);
        return requestScope;
    }

    private Callable1<HttpHandler, Response> requestLessStyle() {
        return new Callable1<HttpHandler, Response>() {
            @Override
            public Response call(HttpHandler handler) throws Exception {
                return handler.handle(RequestBuilder.get("stylesheets/style.less").build());
            }
        };
    }
}

TODO:

  • ActivateSiteMeshModule
  • SiteMeshModule
  • AuditModule
  • PerformanceModule
  • ProfilingModule

Clone this wiki locally