Dropwizard – Easy REST

Share

1.    Šta je Dropwizard

Svi smo svedoci rasta popularnosti arhitekture bazirane na Mikroservisima. Dropwizard ovde zauzima veoma važno mesto. To je frejmvork za razvoj RESTful web servisa, ili bolje rečeno, set alata i frejmvorka za razvoj RESTful web servisa.

Dropwizard omogućava programeru veoma brz inicijalni setap projekta (takozvani project bootstrap). Ovo pomaže da se aplikacija upakuje na način koji omogućava njeno lako instaliranje na produkcino okruženje kao samostalnog servisa. Ako ste ikada bili u situaciji da razvijate REST servis u Springu na primer, verovatno znate koliko može biti naporno postaviti kostur servisa. Sa Dropwizardom, u pitanju je bukvalno dodavanje jedne Maven dependency konfiguracije.

2.    Dropwizard podrazumevane biblioteke

Umesto da u projekat uključujete sve biblioteke potrebne za razvoj REST servisa, I da svaku od njih zasebno konfigurišete, Dropwizard to radi za vas. Evo liste biblioteka koje dolaze uz Dropwizard u osnovnom paketu.

Jetty : Potreban vam je HTTP server da pokrenete Web aplikaciju. Dropwizard poseduje Jetty servlet container za pokretanje Web aplikacija. Umesto da vašu aplikaciju kopirate na aplikacioni server ili web server Dropwizard definiše main metodu koja pokreće Jetty server kao zaseban process. Dropwizard preporučuje samo koričćenje Jetty servera za pokretanje aplikacija, ostali serveri kao što su Tomcat nisu zvanično podržani.

Jersey : Jersey je jedna od najboljih REST API implementacija na tržištu. Takođe, Jersey prati JAX-RSX standard, I Dropwizard ga koristi kao podrazumevani alat za razvoj RESTful web aplikacija.

Jackson : Jackson je svakako postao standard kada je u pitanju upravljanje podacima u JSON format. To je jedan od najboljih API-ja za object mapping za JSON format.

Metrics : Dropwizard ima svoj metrics modul koji nam omogućava čitanje metrika aplikacije kroz HTTP endpoint-e.

Guava : Guava nam daje veliki broj klasa koje ubrzavaju i olakšavaju Java razvoj.

Logback and Slf4j : Ove dve biblioteke se koriste za kvalitetnije logovanje.

Freemarker and Mustache : Izbor templejt procesora je jedna od važnijih odluka. Dropwizard koristi dobro poznate i popularne procesore Freemarker i Mustache za kreiranje korisničkih interfejsa.
Pored gore navedenog, tu sui  mnoge druge biblioteke kao što su Joda Time, Apache HTTP Client i Hibernate Validator koji se takođe koriste za razvoj Dropwizard REST servisa.

3.    Maven konfiguracija

Dropwizard zvanično podržava Maven. U principu, mogu se koristiti i drugi alati, ali je većina dokumentacije i tutorijala napisana tako da se koristi Maven, tako da ćemo ga i mi ovde koristiti.
Ovo je prvi korak u kreiranju vase Dropwizard aplikacije. Dodajte sledeće u Maven pom.xml datoteku:

<dependencies>
    <dependency>
        <groupId>io.dropwizard</groupId>
        <artifactId>dropwizard-core</artifactId>
        <version>${dropwizard.version}</version>
    </dependency>
</dependencies>

Pre toga, trebate definisati i verziju Dropwizarda koju ćete koristiti. Trenutno aktuelna verzija je 1.1.0. To ćete uraditi dodavanjem sledeće konfiguracije u pom.xml:

<properties>
    <dropwizard.version>1.1.0</dropwizard.version>
</properties>

To je to! Uspešno ste konfigurisali Maven. Ova konfiguracija će se pobrinuti da se sve potrebne biblioteke importuju u vaš projekat. Sada ćemo krenuti sa pisanjem naše Dropwizard aplikacije.

4.    Definisanje konfiguracione klase

Dropwizard čuva konfiguraciju u YML datotekama. Potrebno je da imate configuration.yml datoteku u korenom direktorijumu vaše aplikacije. Ova datoteka će biti deserijalizovana i validirana u instancu konfiguracione klase vaše aplikacije. Konfiguraciona klasa vaše aplikacije je podklasa Dropwizardove konfiguracione klase (io.dropwizard.Configuration).
Napravimo jednostavnu konfiguracionu klasu:

import io.dropwizard.Configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;

public class DropwizardBlogConfiguration extends Configuration {
  @NotEmpty
  private String message;
  @NotEmpty
  private String defaultParam1;
  @NotEmpty
  private String defaultParam2;

  @JsonProperty
  public String getMessage() {
    return message;
  }

  @JsonProperty
  public void setMessage(String message) {
    this.message = message;
  }

  @JsonProperty
  public String getDefaultParam1() {
    return defaultParam1;
  }

  @JsonProperty
  public void setDefaultParam1(String defaultParam1) {
    this.defaultParam1 = defaultParam1;
  }

  @JsonProperty
  public String getDefaultParam2() {
    return defaultParam2;
  }

  @JsonProperty
  public void setDefaultParam2(String defaultParam2) {
    this.defaultParam2 = defaultParam2;
  }
}

YAML konfiguracioni fajl bi izgledao ovako:

message: Hello %s! You are learning %s!!
defaultParam1: Programmer
defaultParam2: Dropwizard

Gore pomenuta klasa će biti deserijalizovana iz YAML datoteke i vrednosti polja će biti popunjeni onako kako su konfigurisani u YAML datoteci.

5.    Definisanje glavne aplikacione klase

Sada bi trebalo kreirati glavnu aplikacionu klasu. Ova klasa će podići sve neophodne module i pripremiti naš servis za korišćenje.
Evo jednostavnog primera aplikacione klase:

import io.dropwizard.Application;
import io.dropwizard.setup.Environment;

import com.endava.blog.config.DropwizardBlogConfiguration;
import com.endava.blog.resource.DropwizardBlogResource;

public class DropwizardBlogApplication extends Application<DropwizardBlogConfiguration> {
  public static void main(String[] args) throws Exception {
    new DropwizardBlogApplication().run(args);
  }

  @Override
  public void run(DropwizardBlogConfiguration configuration,
                  Environment environment) {
      final DropwizardBlogResource resource = new DropwizardBlogResource (
          configuration.getMessage(),
          configuration.getDefaultParam1(),configuration.getDefaultParam2());
      environment.jersey().register(resource);
  }
}

6.   Definisanje reprezentacione klase

Sada bi trebalo razmisliti o našem REST API servisu i načinu na koji će resursi biti reprezentovani. Treba da dizajniramo JSON format i da definišemo odgovarajuću reprezentacionu klasu koja će se pobrinuti da podaci budu u željenom formatu. Pogledajmo primer JSON formata koji ćemo koristiti u ovom primeru:

{

  {"content":"Hello Programmer! You are learning Dropwizard!!"}

}

Da postignemo ovaj format, koristićemo sledeću implementaciju reprezentacione klase:

import com.fasterxml.jackson.annotation.JsonProperty;

import org.hibernate.validator.constraints.Length;


public class Representation {

  @Length(max = 3)

  private String content;


  public Representation() {

    // Jackson deserialization

  }


  @JsonProperty

  public String getContent() {

    return content;

  }


  public void setContent(String content) {

    this.content = content;

  }


  public Representation(String content) {

    this.content = content;

  }

}

Ovo je relativno jednostavan POJO model.

7.    Definisanje Dropwizard resursa

Resursi su suština Dropwizarda. Resursi su zapravo definicije endpoint URI-ja našeg servisa kojima se može pristupiti preko HTTP protokola. U ovom primeru, napravićemo resurs klasu sa par anotacija za mapiranje HTTP zahteva. Obzirom da Dropwizard koristi JAX-RS implementaciju, za definisanje putanje koristićemo @Path anotaciju.

Evo naše resurs klase za ovaj primer:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import com.codahale.metrics.annotation.Timed;
import com.endava.blog.representation.Representation;
import com.google.common.base.Optional;

@Path("/our-endpoint")
@Produces(MediaType.APPLICATION_JSON)
public class DropwizardBlogResource {
  private final String message;
  private final String defaultParam1;
  private final String defaultParam2;

  public DropwizardBlogResource(String message, String defaultParam1, String defaultParam2) {
    this.message = message;
    this.defaultParam1 = defaultParam1;
    this.defaultParam2 = defaultParam2;
  }

  @GET
  @Timed
  public Representation sayHello(@QueryParam("param1") Optional<String> param1,
      @QueryParam("param2") Optional<String> param2) {
    final String value = String.format(message, param1.or(defaultParam1), param2.or(defaultParam2));
    return new Representation(value);
  }
}

8.    Registrovanje resursa

Sada je potrebno da gore napravljenu resurs klasu registrujemo kroz aplikacionu klasu. Kao što je pomenuto, aplikaciona klasa služi da inicijalizuje naš servis i sve neophodne module, tako da sve resurse treba ovde registrovati da bi isti bili incijalizovani pri podizanju servisa. U aplikacionu klasu treba dodati sledeće:

@Override
public void run(DropwizardBlogConfiguration configuration,
                Environment environment) {
    final DropwizardBlogResource resource = new DropwizardBlogResource (
        configuration.getMessage(),
        configuration.getDefaultParam1(),configuration.getDefaultParam2());
    environment.jersey().register(resource);
}

9.    Bildovanje aplikacije

Prepručljivo je napraviti jedan takozvani FAT JAR koji će sadržati sve neophodne .class datoteke potrebne za pokretanje aplikacije. Taj JAR može biti instaliran na raličita okruženja od testnog do produkcionog bez bilo kakvih izmena. Da bismo napravili naš JAR treba da konfiguripemo Maven plugin maven-shade. U naš pom.xml treba dodati sledeću konfiguraciju. Rezultujući Maven fajl bi izgledao otprilike ovako:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.endava</groupId>
  <artifactId>dropwizard-blog</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Dropwizard Blog example</name>

  <properties>
    <dropwizard.version>1.1.0</dropwizard.version>
  </properties>

  <dependencies>
    <dependency>
    <groupId>io.dropwizard</groupId>
    <artifactId>dropwizard-core</artifactId>
    <version>${dropwizard.version}</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-shade-plugin</artifactId>
      <version>2.3</version>
      <configuration>
        <createDependencyReducedPom>true</createDependencyReducedPom>
        <filters>
        <filter>
            <artifact>*:*</artifact>
          <excludes>
            <exclude>META-INF/*.SF</exclude>
            <exclude>META-INF/*.DSA</exclude>
            <exclude>META-INF/*.RSA</exclude>
          </excludes>
        </filter>
        </filters>
      </configuration>
      <executions>
        <execution>
        <phase>package</phase>
        <goals>
          <goal>shade</goal>
        </goals>
        <configuration>
          <transformers>
            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>com.endava.blog.DropwizardBlogApplication</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

10.    Pokretanje aplikacije

Sada bi smo trebali biti u mogućnosti da naš servis i pokrenemo. Ako ste uspešno napravili vaš JAR, sve što treba da uradite je da otvorite komandnu liniju i izvršite sledeću komandu da biste izvršili JAR datoteku:

java -jar target/dropwizard-blog-1.0.0.jar server configuration.yml

Ako je sve prošlo u redu, trebalo bi da dobijete nešto nalik na:

INFO  [2017-04-17 13:16:07,657] org.eclipse.jetty.util.log: Logging initialized @742ms to org.eclipse.jetty.util.log.Slf4jLog
INFO  [2017-04-17 13:16:07,710] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: /
INFO  [2017-04-17 13:16:07,711] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: /
INFO  [2017-04-17 13:16:07,713] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: /
INFO  [2017-04-17 13:16:07,713] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: /
INFO  [2017-04-17 13:16:07,715] io.dropwizard.server.ServerFactory: Starting DropwizardBlogApplication
INFO  [2017-04-17 13:16:07,773] org.eclipse.jetty.setuid.SetUIDListener: Opened application@3531f3ca{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
INFO  [2017-04-17 13:16:07,773] org.eclipse.jetty.setuid.SetUIDListener: Opened admin@7fcf294e{HTTP/1.1,[http/1.1]}{0.0.0.0:8081}
INFO  [2017-04-17 13:16:07,775] org.eclipse.jetty.server.Server: jetty-9.4.z-SNAPSHOT
INFO  [2017-04-17 13:16:08,115] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources:

    GET     /our-endpoint (com.endava.blog.resource.DropwizardBlogResource)

INFO  [2017-04-17 13:16:08,116] org.eclipse.jetty.server.handler.ContextHandler: Started i.d.j.MutableServletContextHandler@70c0a3d5{/,null,AVAILABLE}
INFO  [2017-04-17 13:16:08,119] io.dropwizard.setup.AdminEnvironment: tasks =

    POST    /tasks/log-level (io.dropwizard.servlets.tasks.LogConfigurationTask)
    POST    /tasks/gc (io.dropwizard.servlets.tasks.GarbageCollectionTask)

WARN  [2017-04-17 13:16:08,120] io.dropwizard.setup.AdminEnvironment:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!    THIS APPLICATION HAS NO HEALTHCHECKS. THIS MEANS YOU WILL NEVER KNOW      !
!     IF IT DIES IN PRODUCTION, WHICH MEANS YOU WILL NEVER KNOW IF YOU'RE      !
!    LETTING YOUR USERS DOWN. YOU SHOULD ADD A HEALTHCHECK FOR EACH OF YOUR    !
!         APPLICATION'S DEPENDENCIES WHICH FULLY (BUT LIGHTLY) TESTS IT.       !

Sada imate vašu Dropwizard aplikaciju koja sluša na portu 8080 za aplikacione in a portu 8081 za administrativne zahteve.
Primetićete da smo za pokretanje koristili argumente server i configuration.yml čime smo serveru rekli koju konfiguracionu datoteku da koristi.
Vašoj aplikaciji možete pristupiti na adresi http://localhost:8080/our-endpoint ili sa parametrima http://localhost:8080/our-endpoint?param1=Petar&param2=Microservices. Trebalo bi da dobijete poruku sa ili bez prosleđenih paramtara u zavisnosti od vašeg poziva.

Odlično!! Konačno ste implementirali mikroservis koristeći Dropwizard. Čestitam!

11.    Kako promeniti Context Path

Podrazumevano ponašanje Dropwizarda je da svoje endpointe pokreće na / putanji. Dakle, ako ne napomenete nikakav context path za vašu aplikaciju, podrazumeva se da se aplikaciji može pristupiti na http://localhost:8080/. Ako ipak želite to da promenite, možete kofigurisati drugačiju putanju tako što ćete u ZAML datoteku dodati sledeće:

server:
      applicationContextPath: /application

12.    Resursi za učenje Dropwizard-a

Evo nekih korisnih linkova ka tutorijalima i dokumentaciji za Dropwizard koja će vam pomoći da naučite ovaj frejmvork.

•     Official Documentation
•    Dropwizard Metrics
•    Developing RESTful Web Services Using Dropwizard
•    Build a RESTful stub server with Dropwizard
•    Official YAML Site

Share

Prijavi se da prvi dobijaš nove blogove i vesti.

Ostavite odgovor

Dušan Simonović

Senior Java Inzenjer @Endava
mm

Diplomirao i zavrsio Master studije na Fakultetu tehnickih nauka u Novom Sadu na smeru za Mikroracunarsku elektroniku. Ima 7+ godina iskustva kao Java programmer i Sistem administrator i arhitekta.

Trenutno radi u Endavi na Adobe Social projektu kao Senior Java inzenjer i Scrum master.

Sto se prethodnih velikih projekata tice, radio je na softverskom resenju za pracenje cena konkurencije na Internet prodavnicama, takodje radio je na softverskom resenju za prepoznavanje emitovanja fonograma i generisanje izvestaja, kao i na Intranet portalu za vodeceg svajcarskog turistickog operatera.

Prijavi se da prvi dobijaš nove blogove i vesti.

Kategorije