Olemme kaikki todistamassa mikropalveluarkkitehtuurien suosion kasvua. Mikropalveluarkkitehtuurissa Dropwizardilla on erittäin tärkeä paikka. Se on kehys RESTful-verkkopalvelujen tai tarkemmin sanottuna joukon työkaluja ja puitteet RESTful-verkkopalvelujen luomiseen.
Sen avulla kehittäjät voivat käynnistää projektin nopeammin. Tämän avulla voit pakata sovelluksesi siten, että ne voidaan helposti ottaa käyttöön tuotantoympäristössä erillisinä palveluina. Jos olet joskus ollut tilanteessa, jossa sinun on aloitettava projekti kehys kevät Esimerkiksi tiedät todennäköisesti kuinka tuskallista se voi olla.
Dropwizardin avulla on vain lisättävä Maven-riippuvuus.
Tässä blogissa opastan sinut läpi yksinkertaisen Dropwizard RESTful -palvelun kirjoittamisen. Kun olemme valmis, meillä on palvelu CRUD-perustoiminnoille 'osina'. Ei ole väliä mitä 'osa' on; Se voi olla mitä tahansa, mutta se tapahtui ensimmäisenä.
Tallennamme tiedot MySQL-tietokantaan käyttämällä JDBI: tä sen hakemiseen ja käytämme seuraavaa päätepisteet :
GET /parts
-hakea kaikki osat DB: stäGET /part/{id}
saadaksesi tietyn osan DB: ltäPOST /parts
-luodaksesi uuden osanPUT /parts/{id}
-Muokata olemassa olevaa osaaDELETE /parts/{id}
-poistaa osan DB: stäKäytämme OAuthia palvelumme todentamiseen ja sitten lisätään siihen joitain yksikötestejä
Sen sijaan, että Dropwizard sisältäisi kaikki kirjastot, joita tarvitaan erillisen REST-palvelun luomiseen ja kunkin konfigurointiin, se tehdään puolestamme. Tässä on luettelo kirjastoista, jotka tulevat oletusarvoisesti Dropwizardin kanssa:
Yllä olevan luettelon lisäksi on monia muita kirjastoja, kuten Joda Time, Liquibase, Apache HTTP Client ja Hibernate Validator, joita Dropwizard käyttää REST-palveluiden luomiseen.
Dropwizard tukee virallisesti Maven . Vaikka voit käyttää muita rakennustyökaluja, useimmissa oppaissa ja asiakirjoissa käytetään Mavenia, joten käytämme sitä myös täällä. Jos et tunne Mavenia, voit viitata tähän Maven-opetusohjelma .
Tämä on ensimmäinen vaihe Dropwizard-sovelluksen luomisessa. Lisää seuraava merkintä tiedostoon pom.xml
Mavenista:
io.dropwizard dropwizard-core ${dropwizard.version}
Ennen edellisen merkinnän lisäämistä voit lisätä dropwizard.versión
kuten se näkyy seuraavassa:
1.1.0
Siinä kaikki. Olet kirjoittanut Maven-kokoonpanon. Tämä lataa kaikki tarvittavat riippuvuudet projektillesi. Nykyinen versio Dropwizard on 1.1.0 , joten käytämme sitä tässä oppaassa.
Nyt voimme siirtyä kirjoittamaan ensimmäisen todellisen Dropwizard-sovelluksen.
Dropwizard tallentaa asetukset tiedostoihin YAML . Tarvitset tiedoston configuration.yml
sovelluksen juurikansiossa. Tämä tiedosto deserialisoidaan sovelluksen kokoonpanoluokan esiintymäksi ja vahvistetaan. Sovelluksesi määritystiedosto on Dropwizard-määritysluokan (io.dropwizard.Configuration
) alaluokka.
Luodaan yksinkertainen kokoonpanoluokka:
import javax.validation.Valid; import javax.validation.constraints.NotNull; import com.fasterxml.jackson.annotation.JsonProperty; import io.dropwizard.Configuration; import io.dropwizard.db.DataSourceFactory; public class DropwizardBlogConfiguration extends Configuration { private static final String DATABASE = 'database'; @Valid @NotNull private DataSourceFactory dataSourceFactory = new DataSourceFactory(); @JsonProperty(DATABASE) public DataSourceFactory getDataSourceFactory() { return dataSourceFactory; } @JsonProperty(DATABASE) public void setDataSourceFactory(final DataSourceFactory dataSourceFactory) { this.dataSourceFactory = dataSourceFactory; } }
YAML-määritystiedosto näyttäisi tältä:
database: driverClass: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost/dropwizard_blog user: dropwizard_blog password: dropwizard_blog maxWaitForConnection: 1s validationQuery: 'SELECT 1' validationQueryTimeout: 3s minSize: 8 maxSize: 32 checkConnectionWhileIdle: false evictionInterval: 10s minIdleTime: 1 minute checkConnectionOnBorrow: true
Yllä oleva luokka poistetaan käytöstä YAML-tiedostosta ja laitetaan YAML-tiedoston arvot tähän objektiin.
Nyt meidän on mentävä luomaan pääsovellusluokka. Tämä luokka kerää kaikki paketit, ottaa sovelluksen ja asentaa sen käyttöön.
Tässä on esimerkki Dropwizard-sovellusluokasta:
import io.dropwizard.Application; import io.dropwizard.auth.AuthDynamicFeature; import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter; import io.dropwizard.setup.Environment; import javax.sql.DataSource; import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; import org.skife.jdbi.v2.DBI; import com.toptal.blog.auth.DropwizardBlogAuthenticator; import com.toptal.blog.auth.DropwizardBlogAuthorizer; import com.toptal.blog.auth.User; import com.toptal.blog.config.DropwizardBlogConfiguration; import com.toptal.blog.health.DropwizardBlogApplicationHealthCheck; import com.toptal.blog.resource.PartsResource; import com.toptal.blog.service.PartsService; public class DropwizardBlogApplication extends Application { private static final String SQL = 'sql'; private static final String DROPWIZARD_BLOG_SERVICE = 'Dropwizard blog service'; private static final String BEARER = 'Bearer'; public static void main(String[] args) throws Exception { new DropwizardBlogApplication().run(args); } @Override public void run(DropwizardBlogConfiguration configuration, Environment environment) { // Datasource configuration final DataSource dataSource = configuration.getDataSourceFactory().build(environment.metrics(), SQL); DBI dbi = new DBI(dataSource); // Register Health Check DropwizardBlogApplicationHealthCheck healthCheck = new DropwizardBlogApplicationHealthCheck(dbi.onDemand(PartsService.class)); environment.healthChecks().register(DROPWIZARD_BLOG_SERVICE, healthCheck); // Register OAuth authentication environment.jersey() .register(new AuthDynamicFeature(new OAuthCredentialAuthFilter.Builder() .setAuthenticator(new DropwizardBlogAuthenticator()) .setAuthorizer(new DropwizardBlogAuthorizer()).setPrefix(BEARER).buildAuthFilter())); environment.jersey().register(RolesAllowedDynamicFeature.class); // Register resources environment.jersey().register(new PartsResource(dbi.onDemand(PartsService.class))); } }
Aiemmin tekemämme on Dropwizardin suoritusmenetelmän ohittaminen. Tässä menetelmässä me välitämme yhteyden DB (Tietokanta), mukautetun terveystarkastuksen rekisteröinti (puhumme siitä myöhemmin), OAuth-todennuksen alustaminen palveluksellemme ja lopuksi Dropwizard-resurssin rekisteröinti.
Kaikki tämä selitetään myöhemmin.
Nyt meidän on alettava ajatella REST-sovellusliittymäämme ja mitä resurssimme edustaa. Meidän on suunniteltava JSON-muoto ja vastaava renderöintiluokka, joka muunnetaan haluttuun JSON-muotoon.
Tarkastellaan tämän yksinkertaisen edustusluokan esimerkin JSON-muotoa:
{ 'code': 200, 'data': { 'id': 1, 'name': 'Part 1', 'code': 'PART_1_CODE' } }
Yllä olevalle JSON-muodolle luomme renderöintiluokan seuraavasti:
import org.hibernate.validator.constraints.Length; import com.fasterxml.jackson.annotation.JsonProperty; public class Representation { private long code; @Length(max = 3) private T data; public Representation() { // Jackson deserialization } public Representation(long code, T data) { this.code = code; this.data = data; } @JsonProperty public long getCode() { return code; } @JsonProperty public T getData() { return data; } }
Tämä on POJO hyvin yksinkertaisella tavalla.
Resurssi on se, mihin REST-palvelut perustuvat. Se ei ole muuta kuin URI päätepiste päästäksesi palvelimen resurssiin. Tässä esimerkissä meillä on resurssiluokka, jossa on vähän merkintöjä pyynnön URI-kartoitusta varten. Koska Dropwizard käyttää JAX-RS-toteutusta, määritämme URI-polun käyttämällä @Path
-merkintää.
Tässä näytän sinulle resurssiluokan Dropwizard-esimerkillemme:
import java.util.List; import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.eclipse.jetty.http.HttpStatus; import com.codahale.metrics.annotation.Timed; import com.toptal.blog.model.Part; import com.toptal.blog.representation.Representation; import com.toptal.blog.service.PartsService; @Path('/parts') @Produces(MediaType.APPLICATION_JSON) @RolesAllowed('ADMIN') public class PartsResource { private final PartsService partsService;; public PartsResource(PartsService partsService) { this.partsService = partsService; } @GET @Timed public Representation getParts() { return new Representation(HttpStatus.OK_200, partsService.getParts()); } @GET @Timed @Path('{id}') public Representation getPart(@PathParam('id') final int id) { return new Representation(HttpStatus.OK_200, partsService.getPart(id)); } @POST @Timed public Representation createPart(@NotNull @Valid final Part part) { return new Representation(HttpStatus.OK_200, partsService.createPart(part)); } @PUT @Timed @Path('{id}') public Representation editPart(@NotNull @Valid final Part part, @PathParam('id') final int id) { part.setId(id); return new Representation(HttpStatus.OK_200, partsService.editPart(part)); } @DELETE @Timed @Path('{id}') public Representation deletePart(@PathParam('id') final int id) { return new Representation(HttpStatus.OK_200, partsService.deletePart(id)); } }
Näet kuinka kaikki päätepisteet ne on itse asiassa määritelty tässä luokassa.
Palaan nyt sovelluksen pääluokkaan. Tämän luokan lopussa näet, että olemme rekisteröineet palvelumme suoritettavaksi alustettavan resurssimme. Meidän on tehtävä se kaikilla resursseilla, jotka meillä voi olla sovelluksessamme. Tämä on siitä vastuussa oleva koodinpätkä:
// Register resources environment.jersey().register(new PartsResource(dbi.onDemand(PartsService.class)));
Oikeiden poikkeusten käsittelyn ja kyvyn olla riippumaton tietojen tallennusmoottorista käyttöön ottamalla käyttöön 'keskitason' palveluluokan. Tätä luokkaa kutsumme resurssikerroksestamme riippumatta siitä, mikä sen taustalla on. Siksi meillä on tämä erityinen kerros resurssikerrosten ja DAO: n välillä. Tämä on meidän palvelumme:
import java.util.List; import java.util.Objects; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response.Status; import org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException; import org.skife.jdbi.v2.exceptions.UnableToObtainConnectionException; import org.skife.jdbi.v2.sqlobject.CreateSqlObject; import com.toptal.blog.dao.PartsDao; import com.toptal.blog.model.Part; public abstract class PartsService { private static final String PART_NOT_FOUND = 'Part id %s not found.'; private static final String DATABASE_REACH_ERROR = 'Could not reach the MySQL database. The database may be down or there may be network connectivity issues. Details: '; private static final String DATABASE_CONNECTION_ERROR = 'Could not create a connection to the MySQL database. The database configurations are likely incorrect. Details: '; private static final String DATABASE_UNEXPECTED_ERROR = 'Unexpected error occurred while attempting to reach the database. Details: '; private static final String SUCCESS = 'Success...'; private static final String UNEXPECTED_ERROR = 'An unexpected error occurred while deleting part.'; @CreateSqlObject abstract PartsDao partsDao(); public List getParts() { return partsDao().getParts(); } public Part getPart(int id) { Part part = partsDao().getPart(id); if (Objects.isNull(part)) { throw new WebApplicationException(String.format(PART_NOT_FOUND, id), Status.NOT_FOUND); } return part; } public Part createPart(Part part) { partsDao().createPart(part); return partsDao().getPart(partsDao().lastInsertId()); } public Part editPart(Part part) { if (Objects.isNull(partsDao().getPart(part.getId()))) { throw new WebApplicationException(String.format(PART_NOT_FOUND, part.getId()), Status.NOT_FOUND); } partsDao().editPart(part); return partsDao().getPart(part.getId()); } public String deletePart(final int id) { int result = partsDao().deletePart(id); switch (result) { case 1: return SUCCESS; case 0: throw new WebApplicationException(String.format(PART_NOT_FOUND, id), Status.NOT_FOUND); default: throw new WebApplicationException(UNEXPECTED_ERROR, Status.INTERNAL_SERVER_ERROR); } } public String performHealthCheck() { try { partsDao().getParts(); } catch (UnableToObtainConnectionException ex) { return checkUnableToObtainConnectionException(ex); } catch (UnableToExecuteStatementException ex) { return checkUnableToExecuteStatementException(ex); } catch (Exception ex) { return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage(); } return null; } private String checkUnableToObtainConnectionException(UnableToObtainConnectionException ex) { if (ex.getCause() instanceof java.sql.SQLNonTransientConnectionException) { return DATABASE_REACH_ERROR + ex.getCause().getLocalizedMessage(); } else if (ex.getCause() instanceof java.sql.SQLException) { return DATABASE_CONNECTION_ERROR + ex.getCause().getLocalizedMessage(); } else { return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage(); } } private String checkUnableToExecuteStatementException(UnableToExecuteStatementException ex) { if (ex.getCause() instanceof java.sql.SQLSyntaxErrorException) { return DATABASE_CONNECTION_ERROR + ex.getCause().getLocalizedMessage(); } else { return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage(); } } }
Viimeinen osa tästä on itse asiassa terveystarkastuksen toteutus, josta puhumme myöhemmin.
Dropwizard on yhteensopiva JDBI: n ja horrostilan kanssa. Se on erillinen Maven-moduuli, joten lisäämme sen ensin riippuvuudeksi sekä MySQL-liittimen
io.dropwizard dropwizard-jdbi ${dropwizard.version} mysql mysql-connector-java ${mysql.connector.version}
Yksinkertaiselle CRUD-palvelulle suosin henkilökohtaisesti JDBI: tä, koska se on yksinkertaisempi ja paljon nopeampi toteuttaa. Olen luonut yksinkertaisen MySQL-skeeman, jossa on taulukko vain käytettäväksi esimerkissämme. Löydät käsikirjoituksen sen sisällä alkuperän kaavalle. JDBI tarjoaa yksinkertaisen kysymysten kirjoittamisen käyttämällä merkintöjä, kuten @SqlQuery lukemiseen ja @SqlUpdate tietojen kirjoittamiseen. Tässä on DAO-käyttöliittymä:
import java.util.List; import org.skife.jdbi.v2.sqlobject.Bind; import org.skife.jdbi.v2.sqlobject.BindBean; import org.skife.jdbi.v2.sqlobject.SqlQuery; import org.skife.jdbi.v2.sqlobject.SqlUpdate; import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper; import com.toptal.blog.mapper.PartsMapper; import com.toptal.blog.model.Part; @RegisterMapper(PartsMapper.class) public interface PartsDao { @SqlQuery('select * from parts;') public List getParts(); @SqlQuery('select * from parts where id = :id') public Part getPart(@Bind('id') final int id); @SqlUpdate('insert into parts(name, code) values(:name, :code)') void createPart(@BindBean final Part part); @SqlUpdate('update parts set name = coalesce(:name, name), code = coalesce(:code, code) where id = :id') void editPart(@BindBean final Part part); @SqlUpdate('delete from parts where id = :id') int deletePart(@Bind('id') final int id); @SqlQuery('select last_insert_id();') public int lastInsertId(); }
Kuten näette, se on melko yksinkertainen. Meidän on kuitenkin kartoitettava SQL-tulosjoukot malliksi, joka tapahtuu rekisteröimällä luokka kartoittaja . Tässä on luokkamme kartoittaja :
import java.sql.ResultSet; import java.sql.SQLException; import org.skife.jdbi.v2.StatementContext; import org.skife.jdbi.v2.tweak.ResultSetMapper; import com.toptal.blog.model.Part; public class PartsMapper implements ResultSetMapper { private static final String ID = 'id'; private static final String NAME = 'name'; private static final String CODE = 'code'; public Part map(int i, ResultSet resultSet, StatementContext statementContext) throws SQLException { return new Part(resultSet.getInt(ID), resultSet.getString(NAME), resultSet.getString(CODE)); } }
Ja mallimme:
import org.hibernate.validator.constraints.NotEmpty; public class Part { private int id; @NotEmpty private String name; @NotEmpty private String code; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public Part() { super(); } public Part(int id, String name, String code) { super(); this.id = id; this.name = name; this.code = code; } }
Dropwizard tarjoaa natiivia tukea terveystarkastuksiin. Meidän tapauksessamme aiomme tarkistaa, toimiiko tietokanta, ennen kuin sanomme palvelumme olevan terveellistä. Me teemme yksinkertaisia tietokantatoimintoja, kuten osien hankkiminen tietokannasta ja mahdollisten tulosten (onnistumisten tai poikkeusten) käsittely.
Tässä on terveystarkastusten toteutus Dropwizardissa:
import com.codahale.metrics.health.HealthCheck; import com.toptal.blog.service.PartsService; public class DropwizardBlogApplicationHealthCheck extends HealthCheck { private static final String HEALTHY = 'The Dropwizard blog Service is healthy for read and write'; private static final String UNHEALTHY = 'The Dropwizard blog Service is not healthy. '; private static final String MESSAGE_PLACEHOLDER = '{}'; private final PartsService partsService; public DropwizardBlogApplicationHealthCheck(PartsService partsService) { this.partsService = partsService; } @Override public Result check() throws Exception { String mySqlHealthStatus = partsService.performHealthCheck(); if (mySqlHealthStatus == null) { return Result.healthy(HEALTHY); } else { return Result.unhealthy(UNHEALTHY + MESSAGE_PLACEHOLDER, mySqlHealthStatus); } } }
Dropwizard tukee perustodennusta ja OAuth . Tässä näytän sinulle, kuinka suojaat palvelusi OAuthilla. Monimutkaisuuden vuoksi olen kuitenkin jättänyt pois taustalla olevan tietokantarakenteen ja näytän vain, kuinka se kehittyy. Täyden mittakaavan käyttöönoton ei pitäisi olla ongelma tästä. Dropwizardilla on kaksi tärkeää rajapintaa, jotka meidän on toteutettava.
Ensimmäinen on Todentaja . Luokan on toteutettava authenticate
-menetelmä, jonka on tarkistettava, onko annettu pääsytunniste kelvollinen. Joten kutsuisin tätä sovelluksen ensimmäiseksi oveksi. Jos se onnistuu, sen pitäisi johtaa päämieheen. Tämä päämies on todellinen käyttäjä roolillaan. Tämä on tärkeää toiselle Dropwizard-käyttöliittymälle, joka meidän on toteutettava. Tämä on Valtuuttaja ja se on vastuussa siitä, onko käyttäjällä riittävät oikeudet käyttää tiettyä resurssia. Joten jos palaat takaisin ja tutustu resurssiluokkaamme, huomaat, että sen käyttö edellyttää järjestelmänvalvojan roolia päätepisteet . Nämä merkinnät voivat olla myös menetelmällä. Dropwizard-valtuutustuki on erillinen Maven-moduuli, joten meidän on lisättävä se riippuvuuksiin:
io.dropwizard dropwizard-auth ${dropwizard.version}
Tässä ovat esimerkkimme luokat, jotka eivät todellakaan tee mitään fiksua, mutta ovat luuranko laajamittaiselle OAuth-valtuutukselle:
import java.util.Optional; import io.dropwizard.auth.AuthenticationException; import io.dropwizard.auth.Authenticator; public class DropwizardBlogAuthenticator implements Authenticator { @Override public Optional authenticate(String token) throws AuthenticationException { if ('test_token'.equals(token)) { return Optional.of(new User()); } return Optional.empty(); } }
import java.util.Objects; import io.dropwizard.auth.Authorizer; public class DropwizardBlogAuthorizer implements Authorizer { @Override public boolean authorize(User principal, String role) { // Allow any logged in user. if (Objects.nonNull(principal)) { return true; } return false; } }
import java.security.Principal; public class User implements Principal { private int id; private String username; private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String getName() { return username; } }
Aiomme lisätä joitain yksikkötestejä sovellukseemme. Pidän kiinni Dropwizard-koodin tiettyjen osien testaamisesta, tapauksessamme: Edustus ja resurssi. Meidän on lisättävä seuraavat riippuvuudet Maven-tiedostoon:
io.dropwizard dropwizard-testing ${dropwizard.version} org.mockito mockito-core ${mockito.version} test
Hahmonnuksen testaamiseksi tarvitsemme myös JSON-näytetiedoston testattavaksi. Joten luodaan fixtures/part.json
alle src/test/resources
:
{ 'id': 1, 'name': 'testPartName', 'code': 'testPartCode' }
Ja tässä on JUnit-testiluokka:
import static io.dropwizard.testing.FixtureHelpers.fixture; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; import com.fasterxml.jackson.databind.ObjectMapper; import com.toptal.blog.model.Part; import io.dropwizard.jackson.Jackson; public class RepresentationTest { private static final ObjectMapper MAPPER = Jackson.newObjectMapper(); private static final String PART_JSON = 'fixtures/part.json'; private static final String TEST_PART_NAME = 'testPartName'; private static final String TEST_PART_CODE = 'testPartCode'; @Test public void serializesToJSON() throws Exception { final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE); final String expected = MAPPER.writeValueAsString(MAPPER.readValue(fixture(PART_JSON), Part.class)); assertThat(MAPPER.writeValueAsString(part)).isEqualTo(expected); } @Test public void deserializesFromJSON() throws Exception { final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE); assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getId()).isEqualTo(part.getId()); assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getName()) .isEqualTo(part.getName()); assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getCode()) .isEqualTo(part.getCode()); } }
Resurssien testaamisen osalta Dropwizardin testauksen pääkohde on, että se todella käyttäytyy HTTP-asiakkaana ja lähettää HTTP-pyyntöjä resursseja vastaan. Siksi et testaa menetelmiä kuten tavallisesti tavallisessa tapauksessa. Tässä on esimerkki luokastamme PartsResource
:
public class PartsResourceTest { private static final String SUCCESS = 'Success...'; private static final String TEST_PART_NAME = 'testPartName'; private static final String TEST_PART_CODE = 'testPartCode'; private static final String PARTS_ENDPOINT = '/parts'; private static final PartsService partsService = mock(PartsService.class); @ClassRule public static final ResourceTestRule resources = ResourceTestRule.builder().addResource(new PartsResource(partsService)).build(); private final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE); @Before public void setup() { when(partsService.getPart(eq(1))).thenReturn(part); List parts = new ArrayList(); parts.add(part); when(partsService.getParts()).thenReturn(parts); when(partsService.createPart(any(Part.class))).thenReturn(part); when(partsService.editPart(any(Part.class))).thenReturn(part); when(partsService.deletePart(eq(1))).thenReturn(SUCCESS); } @After public void tearDown() { reset(partsService); } @Test public void testGetPart() { Part partResponse = resources.target(PARTS_ENDPOINT + '/1').request() .get(TestPartRepresentation.class).getData(); assertThat(partResponse.getId()).isEqualTo(part.getId()); assertThat(partResponse.getName()).isEqualTo(part.getName()); assertThat(partResponse.getCode()).isEqualTo(part.getCode()); verify(partsService).getPart(1); } @Test public void testGetParts() { List parts = resources.target(PARTS_ENDPOINT).request().get(TestPartsRepresentation.class).getData(); assertThat(parts.size()).isEqualTo(1); assertThat(parts.get(0).getId()).isEqualTo(part.getId()); assertThat(parts.get(0).getName()).isEqualTo(part.getName()); assertThat(parts.get(0).getCode()).isEqualTo(part.getCode()); verify(partsService).getParts(); } @Test public void testCreatePart() { Part newPart = resources.target(PARTS_ENDPOINT).request() .post(Entity.entity(part, MediaType.APPLICATION_JSON_TYPE), TestPartRepresentation.class) .getData(); assertNotNull(newPart); assertThat(newPart.getId()).isEqualTo(part.getId()); assertThat(newPart.getName()).isEqualTo(part.getName()); assertThat(newPart.getCode()).isEqualTo(part.getCode()); verify(partsService).createPart(any(Part.class)); } @Test public void testEditPart() { Part editedPart = resources.target(PARTS_ENDPOINT + '/1').request() .put(Entity.entity(part, MediaType.APPLICATION_JSON_TYPE), TestPartRepresentation.class) .getData(); assertNotNull(editedPart); assertThat(editedPart.getId()).isEqualTo(part.getId()); assertThat(editedPart.getName()).isEqualTo(part.getName()); assertThat(editedPart.getCode()).isEqualTo(part.getCode()); verify(partsService).editPart(any(Part.class)); } @Test public void testDeletePart() { assertThat(resources.target(PARTS_ENDPOINT + '/1').request() .delete(TestDeleteRepresentation.class).getData()).isEqualTo(SUCCESS); verify(partsService).deletePart(1); } private static class TestPartRepresentation extends Representation { } private static class TestPartsRepresentation extends Representation { } private static class TestDeleteRepresentation extends Representation { } }
Paras käytäntö on luoda yksi FAR JAR-tiedosto, joka sisältää kaikki sovelluksen suorittamiseen tarvittavat .class-tiedostot. Sama JAR-tiedosto voidaan ottaa käyttöön eri ympäristössä testistä tuotantoon muuttamatta riippuvuuskirjastoja. Voit aloittaa esimerkkisovelluksen rakentamisen a rasvaa JAR, meidän on määritettävä Maven-laajennus nimeltä maven-varjo . Sinun on lisättävä seuraavat merkinnät pom.xml-tiedostosi laajennukset-osioon.
Tässä on esimerkki Maven-kokoonpanosta JAR-tiedoston rakentamiseksi.
4.0.0 com.endava dropwizard-blog 0.0.1-SNAPSHOT Dropwizard Blog example 1.1.0 2.7.12 6.0.6 1.8 1.8 io.dropwizard dropwizard-core ${dropwizard.version} io.dropwizard dropwizard-jdbi ${dropwizard.version} io.dropwizard dropwizard-auth ${dropwizard.version} io.dropwizard dropwizard-testing ${dropwizard.version} org.mockito mockito-core ${mockito.version} test mysql mysql-connector-java ${mysql.connector.version} org.apache.maven.plugins maven-shade-plugin 2.3 true *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA package shade com.endava.blog.DropwizardBlogApplication
Nyt meidän pitäisi pystyä suorittamaan palvelu. Jos olet rakentanut JAR-tiedostosi oikein, sinun on tehtävä avaamalla komentokehote ja suorittamalla seuraava komento JAR-tiedoston suorittamiseksi:
java -jar target/dropwizard-blog-1.0.0.jar server configuration.yml
Jos kaikki meni hyvin, sinun pitäisi nähdä jotain tällaista:
INFO [2017-04-23 22:51:14,471] org.eclipse.jetty.util.log: Logging initialized @962ms to org.eclipse.jetty.util.log.Slf4jLog INFO [2017-04-23 22:51:14,537] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: / INFO [2017-04-23 22:51:14,538] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: / INFO [2017-04-23 22:51:14,681] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: / INFO [2017-04-23 22:51:14,681] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: / INFO [2017-04-23 22:51:14,682] io.dropwizard.server.ServerFactory: Starting DropwizardBlogApplication INFO [2017-04-23 22:51:14,752] org.eclipse.jetty.setuid.SetUIDListener: Opened [email protected] {HTTP/1.1,[http/1.1]}{0.0.0.0:8080} INFO [2017-04-23 22:51:14,752] org.eclipse.jetty.setuid.SetUIDListener: Opened [email protected] {HTTP/1.1,[http/1.1]}{0.0.0.0:8081} INFO [2017-04-23 22:51:14,753] org.eclipse.jetty.server.Server: jetty-9.4.2.v20170220 INFO [2017-04-23 22:51:15,153] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources: GET /parts (com.toptal.blog.resource.PartsResource) POST /parts (com.toptal.blog.resource.PartsResource) DELETE /parts/{id} (com.toptal.blog.resource.PartsResource) GET /parts/{id} (com.toptal.blog.resource.PartsResource) PUT /parts/{id} (com.toptal.blog.resource.PartsResource) INFO [2017-04-23 22:51:15,154] org.eclipse.jetty.server.handler.ContextHandler: Started [email protected] {/,null,AVAILABLE} INFO [2017-04-23 22:51:15,158] io.dropwizard.setup.AdminEnvironment: tasks = POST /tasks/log-level (io.dropwizard.servlets.tasks.LogConfigurationTask) POST /tasks/gc (io.dropwizard.servlets.tasks.GarbageCollectionTask) INFO [2017-04-23 22:51:15,162] org.eclipse.jetty.server.handler.ContextHandler: Started [email protected] {/,null,AVAILABLE} INFO [2017-04-23 22:51:15,176] org.eclipse.jetty.server.AbstractConnector: Started [email protected] {HTTP/1.1,[http/1.1]}{0.0.0.0:8080} INFO [2017-04-23 22:51:15,177] org.eclipse.jetty.server.AbstractConnector: Started [email protected] {HTTP/1.1,[http/1.1]}{0.0.0.0:8081} INFO [2017-04-23 22:51:15,177] org.eclipse.jetty.server.Server: Started @1670ms
Sinulla on nyt oma Dropwizard-sovellus, joka kuuntelee portteja 8080 sovelluspyyntöjä ja 8081 hallintapyyntöjä varten.
Huomaa, että server configuration.yml
Sitä käytetään käynnistämään HTTP-palvelin ja välittämään YAML-määritystiedoston sijainti palvelimelle.
Erinomainen! Lopuksi olemme toteuttaneet mikropalvelun puitteet Dropwizard. Otetaan nyt tauko ja nautitaan kuppi teetä. Teit hyvää työtä.
Voit käyttää mitä tahansa HTTP-asiakasta, kuten POSTMAN tai mitä tahansa muuta. Sinun pitäisi pystyä käyttämään palvelinta painamalla http://localhost:8080/parts
. Sinun pitäisi saada viesti, jossa kerrotaan, että kirjautumistiedot tarvitaan palvelun käyttämiseen. Todentamiseksi lisää otsikko Authorization
arvolla support_test_token
. Jos onnistut, sinun pitäisi nähdä jotain:
{ 'code': 200, 'data': [] }
Mikä tarkoittaa, että tietokanta on tyhjä. Luo ensimmäinen osa vaihtamalla HTTP-menetelmä GET: stä POST: iin ja toimittamalla tämä hyötykuorma:
{ 'name':'My first part', 'code':'code_of_my_first_part' }
Kaikki muut päätepisteet He työskentelevät samalla tavalla, joten jatka pelaamista ja nauti.
Dropwizard-sovellus käynnistyy oletusarvoisesti /
Jos et esimerkiksi mainitse mitään oletusarvoisesti sovelluksen kontekstipolusta, sovellusta voi käyttää URL-osoitteesta http://localhost: 8080/
Jos haluat määrittää oman kontekstipolun sovelluksellesi, lisää seuraavat merkinnät YAML-tiedostoon. ~~~ palvelin: applicationContextPath: / sovellus ~~~
Nyt kun Dropwizard REST -palvelu on asennettu, on aika tehdä yhteenveto Dropwizardin käytön tärkeimmistä eduista tai haitoista, kuten puitteet LEVÄTÄ. Tästä viestistä on aivan selvää, että Dropwizard tarjoaa bootstrap erittäin nopeasti projektissasi. Ja se on luultavasti suurin etu Dropwizardin käytöstä.
Se sisältää myös kaikki huippuluokan kirjastot / työkalut, joita tarvitset palvelun kehittämiseksi. Joten sinun ei todellakaan tarvitse huolehtia siitä. Se antaa sinulle myös erittäin mukavan kokoonpanon hallinnan. Tietenkin Dropwizardilla on myös joitain haittoja. Dropwizardin käyttö on rajoitettu käyttämään mitä Dropwizard tarjoaa tai tukee. Menetät osan vapaudesta, johon saatat olla tottunut kehittäessäsi. Mutta silti en edes kutsuisi sitä haitaksi, koska juuri tämä tekee Dropwizardista sen, mikä se on - helppo konfiguroida, helppo kehittää, mutta silti erittäin vankka ja suorituskykyinen REST-kehys.
Mielestäni monimutkaisuuden lisääminen puitteet tukemalla yhä useampia kolmansien osapuolten kirjastoja se tuo myös tarpeetonta monimutkaisuutta kehitykseen.