Dropwizard är en körmiljö och ett ramverk för att enkelt utveckla små enheter i form av RESTful webtjänster. En stack bestående av bl.a Jetty, Jersey, Jackson, slf4j, JDBI och en föreslagen projektstruktur gör det väldigt lätt att komma igång!

Begrepp

Dropwizard har idenitifierat följande aktörer och entiteter i sin stack:

Configuration

Konfigurationen är till för att hålla miljöspecifika aspekter av tjänsten. Det kan t.ex vara databaskonfiguration eller nödvändiga sökvägar och url:ar. Konfigurationen skrivs i formatet YAML. Genom JSON-annotationer i din klass läser DropWizard automatiskt upp konfigurationen och populerar din konfigurationsklass.

Representation

Detta är de objekt som ska skickas mellan klient och server och är i sin enklaste form vanliga POJOs. Jackson används för serialisering och deserialisering. DropWizard rekommenderar att dessa objekt är immutable.

Resource

Det är resurserna som exponerar REST API:et. Varje resurs och metod har en URI och klassen annoteras med javax.ws.rs.*-typerna för att definiera sökvägar, innehållstyper och metod. DropWizard använder Jersey-implementationen.

Health check

Hälsokontroller är små kodsnuttar som testar att tjänsten är uppe och att allt är som det ska. Status för hälsokontrollerna kan inspekteras via en speciell admin-url. DropWizard skapar automatiskt några hälsokontroller, bl.a en som kontrollerar att databasanslutningen är korrekt (om man har en i sitt projekt).

Service

Servicen ansvarar för att dra igång tjänsten med en konfiguration och med resurserna initialiserade med eventuella beroenden. Detta sker genom en vanlig main-metod(public static void main(String[] args) { ... }).

I vårt exempelprojekt ser serviceklassen ut så här:

public class MainService extends Service<MainConfiguration> {
    public static void main(String[] args) throws Exception {
        new MainService().run(args);
    }
 
    @Override
    public void initialize(
            Bootstrap<MainConfiguration> mainConfigurationBootstrap) {

        mainConfigurationBootstrap.addBundle(
            new AssetsBundle("/assets/", "/"));
    }
 
    @Override
    public void run(
            MainConfiguration config, 
            Environment environment) throws Exception {
 
        DBI dbi = initDatabaseConnection(config, environment);
 
        final EventsRepository eventsRepository = 
            dbi.onDemand(EventsRepository.class);
        final ExpensesRepository expensesRepository = 
            dbi.onDemand(ExpensesRepository.class);
 
        environment.addResource(new EventsResource(eventsRepository));
        environment.addResource(new ExpensesResource(expensesRepository));
    }
 
    private DBI initDatabaseConnection(
            MainConfiguration config, Environment environment) throws ClassNotFoundException {
 
        final DBIFactory factory = new DBIFactory();
        final DBI jdbi = factory.build(
            environment, config.getDatabaseConfiguration(), "db");
 
        createTables(jdbi);
        return jdbi;
    }
 
    private void createTables(DBI dbi) {
        Handle handle = dbi.open();
        handle.execute(
            EventsRepository.CREATE_TABLE_STATEMENT);
        handle.execute(
            ExpensesRepository.CREATE_TABLE_STATEMENT);
        handle.close();
    }
}

För dig som är van vid Spring eller något annat DI-ramverk kan det kännas lite konstigt att själv behöva instansiera t.ex resursklasserna (och skjuta in Repository-implementationerna). Eftersom tanken med en DropWizard tjänst är att ansvara för en relativt liten delmängd av ett system så tycker jag inte att detta är särskilt besvärande.

Resurser, dvs REST-tjänsterna, aktiveras genom att lägga till dem till instansen av Environment.

Komma igång

Checka ut exempelprojektet och bygg:

git clone git://github.com/cygni-stacktrace/dropwizard-sample.git 
cd dropwizard-sample 
mvn package

Starta tjänsten med kommandot server och filnamnet för konfigurationen:

java -jar target/expenses-1.0-SNAPSHOT.jar server development.yml

Rest-API:et ligger under kontextet /api, en GET-operation för att lista events finns t.ex här: http://localhost:8080/api/event

Admingränssnittet med länk till Health check finns här: http://localhost:8081

Testning

Testning av resurser är en enkel historia med DropWizard. Genom att låta din testklass uttöka ResourceTest kan du lägga till resursen under test så här:

@Override
protected void setUpResources() throws Exception { 
    eventsRepository = mock(EventsRepository.class);
    addResource(new EventsResource(eventsRepository));
}

En testmetod:

@Test
public void shouldShowOneEvent() throws IOException { 
    //given
    Event event = new Event(1, "An event", null); 
    given(eventsRepository.findById(event.getId()))
        .willReturn(event); 

    //when
    Event result =
        client().resource("/event/1").get(Event.class); 

    //then
    assertThat(
        "event should be returned", result, is(event)); }

Dropwizard drar igång Jersey och invokerar anropen i testet via en HTTP-klient så hela request/response kedjan går över JSON och HTTP. Detta är bra för då testas även att Jackson kan serialisera och deserialisera dina POJO:s.

Databasstöd

Dropwizard har moduler med stöd för Hibernate och JDBI. I exempelprojektet används JDBI.

Slutsats

Det är väldigt lätt att komma igång med Dropwizard och jag tycker att de valt en vettig stack med hjälpbibliotek. Den föreslagna projektstrukturen och användandet av Maven gör att det fungerar bra i alla stora IDE:er. Att inkludera Health checks direkt från början känns väldigt bra och det är befriande att ha en app som kan köras helt stand-alone (d.v.s den behöver inte deployas i en webcontainer). Jag tycker också att de tänkt till bra kring testningen.

Om man planerar att börja använda Dropwizard i ett större sammanhang behövs dock en strategi för hur tjänster ska kunna versionshanteras samt upptäckas.

Vidare läsning

http://dropwizard.codahale.com