Dropwizard est un framework Java qui est orienté Hautes Performances, Web Services REST et qui produit une application prête à être mise en production en intégrant des services utiles aux "Ops".
Dropwizard est adapté pour la mise en place de micro-services.
En effet, une application produite avec Dropwizard est autonome et autoportée :
- elle embarque un serveur Jetty pour exposer des services RESTful via Jersey (norme JAX-RS)
- elle peut utiliser une base mémoire comme H2
L'application se lance via la commande
java -jar
avec en paramètre la localisation du fichier YAML de configuration.java -jar application.jar server config.ymlLes paramètres de configuration qui sont spécifiques à l'environnement cible peuvent être surchargés via la commande
java -jar
afin de ne pas avoir à modifier à chaque fois le fichier de configuration.Il est également possible de fournir des services pour l'arrêt et le redémarrage de certains services de l'application (
interface Managed
) ainsi que pour le changement de fichiers statiques à la volée (Assets
).La présence de métriques via Metrics permet de fournir des informations aux "Ops" (opérations) :
- état de la JVM
- état des composants critiques de l'application :
- temps de réponses de requêtes, état du pool de connexions JDBC (nombre de connexions actives, en attente), etc.
Les métriques fournies par Metrics peuvent s'interfacer avec des outils de reporting comme Ganglia et Graphite afin d'avoir un tableau de bord pour les "Ops".
Composants fournis avec Dropwizard
Dropwizard intègre et configure automatiquement les composants suivants :- Jetty: Serveur HTTP et Servlet Java
- Jersey: implémentation de la norme JAX-RS de Java EE pour définr des services Web RESTful
- Jackson: Parser JSON pour Java
- JDBI: Librairie pour l'exécution de requêtes SQL
- Logback: Successeur de Log4j
- Metrics: Enregistre et fournit des métriques de la JVM et de l'application
- Metrics permet d'avoir une vue de l'état des composants critiques de l'application dans l'environnement de production
- Pour le reporting, il est possible d'utiliser Ganglia et Graphite
- Google guava: Bibliothèque de classes utilitaires: collections, cache, support des pritimives, concurrence, annotations, traitement de chaînes de caractères, I/O, etc.
- Hibernate Validator: Règles de validation via l'utilisation d'annotations
- Liquibase: Pour gérer les changements de versions de la base de données (dans le module dropwizard-migrations)
Modules Dropwizard
Dropwizard propose les modules suivants :
- dropwizard-core: le coeur de l'application Dropwizard qui vient avec les composants cités ci-dessus: Jetty, Jersey, Jackson, Metrics, Guava, Logback, Hibernate Validator
- dropwizard-client: clients HTTP pour l'intégration avec d'autres web services :
- dropwizard-jdbi: pour intéragir via des requêtes SQL avec la base relationnelle à l'aide de la librairie JDBI
- dropwizard-migrations: ce module utilise Liquibase pour gérer les changements de version de la base de données
- dropwizard-hibernate: permet l'utilisation de l'ORM Hibernate pour gérer la persistance des objets métiers
- dropwizard-auth: fournit les modes d'authentification suivants :
- dropwizard-views: produit les pages HTML à l'aide de :
- dropwizard-testing: test de l'application
- dropwizard-scala: pour implémenter l'application à l'aide du langage Scala
Configuration Maven
<dependencies> <dependency> <groupId>io.dropwizard</groupId> <artifactId>dropwizard-core</artifactId> <version>0.7.1</version> </dependency> ... plus les dépendances vers les autres modules de Dropwizard </dependencies>
Exemple d'application Dropwizard
Le repo Github de Dropwizard contient un exemple d'application de type "Hello World" qui permet de tester une application Dropwizard :L'application d'exemple de Dropwizard utilise H2 comme base de données embarquée et Hibernate pour la persistance des données. Elle expose les services RESTful via Jersey (JAX-RS). Elle utilise Metrics pour fournit des métriques.
Pour utiliser cette application d'exemple :
- Télécharger le ZIP de Dropwizard disponible sur le repo Github de Dropwizard :
- Dans le ZIP, dézipper le répertoire dropwizard-exemple
- Dans ce répertoire dropwizard-exemple:
- Lancer le packaging avec Maven :
mvn package -Djar.finalName=dropwizard-example
java -jar target/dropwizard-example.jar server example.ymlL'application d'exemple de Dropwizard se lance.
Nous pouvons appeler les services REST d'exemple via le navigateur qui affiche le résultat en JSON :
{ id: 1, content: "Hello, Stranger!" }
{ id: 2, content: "Hello, Successful Dropwizard User!" }L'application fournit également des pages d'administration avec des outils accessibles sur un autre port que celui utilisé pour les services REST et les pages de l'application :
- http://localhost:8081
- Metrics : métriques de l'application sous le format JSON
{ version: "3.0.0", gauges: { io.dropwizard.db.ManagedPooledDataSource.hibernate.active: { value: 0 }, io.dropwizard.db.ManagedPooledDataSource.hibernate.idle: { value: 10 }, io.dropwizard.db.ManagedPooledDataSource.hibernate.size: { value: 10 }, io.dropwizard.db.ManagedPooledDataSource.hibernate.waiting: { value: 0 }, ... jvm.memory.heap.init: { value: 16777216 }, jvm.memory.heap.max: { value: 259522560 }, jvm.memory.heap.usage: { value: 0.07287816519689078 }, jvm.memory.heap.used: { value: 18913528 }, ... }
- http://localhost:8081/ping
- Réponse :
pong
- http://localhost:8081/threads
- Réponse:
Reference Handler id=2 state=WAITING - waiting on <0x01e13dbe> (a java.lang.ref.Reference$Lock) - locked <0x01e13dbe> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:503) at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source) Finalizer id=3 state=WAITING - waiting on <0x008f2d36> (a java.lang.ref.ReferenceQueue$Lock) - locked <0x008f2d36> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.Object.wait(Native Method) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source) ...
{ deadlocks: { healthy: true }, hibernate: { healthy: true }, template: { healthy: true } }
Organisation du projet
Un projet Dropwizard s'organise de préférence de la manière suivante :- package
com.myapp
: MyApplicationConfiguration.java
:classe de configurationMyApplication.java
:classe principale de l'application- packages :
core
: domaine métierresources
: services RESTfulview
: rendu via des pages HTMLdb
: DAO - accès à la base de donnéeshealth
: Classes HealthCheck : indique l'état des composants de l'applicationfilter
: classes de filtrage et d'analyse des requêtes : création de règles de validation via des annotationsauth
: authentificationclient
: récupération d'informations d'autres services web
Voir sur le projet d'exemple, l'organisation des packages : organisation du projet d'exemple
Configuration
Le fichier YAML de configuration sera parsé par Jackson pour valoriser les propriétés de la classeHelloWorldConfiguration.java
.
Fichier YAML de configuration
Contenu du fichier YAML de configuration :example.yml
database: driverClass: org.h2.Driver user: sa password: sa url: jdbc:h2:./target/example server: applicationConnectors: - type: http port: 8080 adminConnectors: - type: http port: 8081 logging: level: INFO loggers: com.myapp: DEBUG org.hibernate.SQL: ALL appenders: - type: console
Classe de configuration
Extrait de la classeHelloWorldConfiguration
:public class HelloWorldConfiguration extends Configuration { @Valid @NotNull private DataSourceFactory database = new DataSourceFactory(); @JsonProperty("database") public DataSourceFactory getDataSourceFactory() { return database; } @JsonProperty("database") public void setDataSourceFactory(DataSourceFactory dataSourceFactory) { this.database = dataSourceFactory; } }Les annotations
@JsonProperty("database")
indique à Jackson les méthodes getter et setter à utiliser pour le parsing de la propriété database
du fichier YAML de configuration.
La classe parente
Configuration
contient d'autres annotations pour le parsing des autres paramètres du fichier de configuration YAML :
server
, logging
, metrics
.
Classe principale
La classe principale étend la classeApplication
de Dropwizard qui prend en type générique la classe de configuration qui est ici HelloWorldConfiguration
.Deux méthodes de la classe
Application
sont à surcharger par la classe HelloWorldApplication
de notre application :initialize(Bootstrap b)
:- lecture des paramètres de configuration pour l'initialisation des composants de l'application:
- Connexion à la base de données
- Configuration Hibernate
run(Configuration c, Environnement e)
:- enregistrement des composants de l'application dans l'objet
Environnement
: - services REST
- DAO
- etc.
HelloWorldApplication
:
public class HelloWorldApplication extends Application{ public static void main(String[] args) throws Exception { new HelloWorldApplication().run(args); } // initialisation des composants de l'application à partir des paramètres de la configuration public void initialize(Bootstrap bootstrap) { (...) bootstrap.addBundle(hibernateBundle); } // enregistrement des composants de l'application dans l'objet Environnement public void run(HelloWorldConfiguration configuration, Environment environment) { final PersonDAO dao = new PersonDAO(hibernateBundle.getSessionFactory()); final Template template = configuration.buildTemplate(); environment.jersey().register(new PeopleResource(dao)); environment.jersey().register(new PersonResource(dao)); environment.jersey().register(new HelloWorldResource(template)); } }
Domaine métier
Les classes du domaine métier sont des POJOs et sont persistés par les DAOVoici l'exemple d'un objet persisté par Hibernate :
@Entity @Table(name = "people") @NamedQueries({ @NamedQuery( name = "com.example.helloworld.core.Person.findAll", query = "SELECT p FROM Person p" ) }) public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(name = "fullName", nullable = false) private String fullName; @Column(name = "jobTitle", nullable = false) private String jobTitle; ... }Les annotations ne sont pas obligatoires et dépendent du framework de persistence utilisé.
Dropwizard n'apporte pas de restriction sur le choix de la librairie utilisée et apporte par défaut JDBI (module
dropwizard-jdbi
) et Hibernate (module dropwizard-hibernate
).DAO
La DAO PersonDAO
étend la classe abstraite AbstractDAO
de Dropwizard pour avoir accès à Hibernate au travers de ces méthodes.
public class PersonDAO extends AbstractDAO{ public PersonDAO(SessionFactory factory) { super(factory); } public Optional findById(Long id) { return Optional.fromNullable(get(id)); } public Person create(Person person) { return persist(person); } public List findAll() { return list(namedQuery("com.example.helloworld.core.Person.findAll")); } }
Service REST
La classe du service REST est autonome: il n'y a pas d'adhérence à Dropwizard.
Elle utilise directement les annotations de Jersey: @Path
, @Produces
, @GET
et quelques annotations de Dropwizard : UnitOfWork
, LongParam
.
@Path("/people/{personId}") @Produces(MediaType.APPLICATION_JSON) public class PersonResource { private final PersonDAO peopleDAO; public PersonResource(PersonDAO peopleDAO) { this.peopleDAO = peopleDAO; } @GET @UnitOfWork public Person getPerson(@PathParam("personId") LongParam personId) { return findSafely(personId.get()); } @GET @Path("/view_freemarker") @UnitOfWork @Produces(MediaType.TEXT_HTML) public PersonView getPersonViewFreemarker(@PathParam("personId") LongParam personId) { return new PersonView(PersonView.Template.FREEMARKER, findSafely(personId.get())); } ... }
La DAO PersonDAO
est injectée via le constructeur. Cela a été effectuée dans la méthode run
de la classe principale de l'application HelloWorldApplication
via la ligne :
environment.jersey().register(new PeopleResource(dao));
Documentation
Pour aller plus loin, voici les liens vers la documentation de Dropwizard :Conclusion
Dropwizard est intéressant pour la mise en place de petites applications autonomes et pour la mise en oeuvre de micro services.Il est à comparer avec Spring Boot sachant que de base Dropwizard ne repose pas sur Spring.
Références
A comparer avec Spring Boot :