Introduzione
In questo articolo daremo una sguardo a cosa genera JHipster sia lato server che lato client. Analizzeremo il codice generato per l’applicazione “monolitica” di esempio jhmoka [1]. Si ricorda che JHipster è in grado di creare applicazioni sia tradizionali three tier, cosiddette “monolitiche”, sia applicazioni microservices.
Infatti la prima domanda che JHipster chiede in fase di generazione è il tipo di applicazione che si desidera creare dando la possibilità di scegliere tra due stili di architettura:
- architettura “monolitica” (Monolithic application) che è l’opzione di default “one-size- fits-all” e contiene sia il codice AngularJS front-end che back-end in un unico progetto maven;
- architettura a “microservizi” (Microservice application) che divide il front-end e back-end mediante un gateway (Microservice gateway) in modo che sia più facile scalare su infrastrutture distribuite.
Server side
Iniziamo adesso a vedere cosa JHipster genera dietro le quinte partendo dalla parte server.
La configurazione di base di JHipster realizza una web application con le impostazioni server side utilizzando la configurazione di Spring Boot, e client side con la configurazione AngularJS.
La tecnologia lato server di base, realizzata automaticamente nella Jhipster webapp, utilizza principalmente la convention over configuration di Spring Boot. La struttura dei package rispetta, infatti, quella richiesta da Spring Boot per una più semplice configurazione della webapp, così come la struttura delle cartelle per i file di configurazione non Java [9].
JHipster usa i più famosi moduli Spring come Spring Security, Spring Data JPA e Spring MVC. JHipster inoltre imposta tutta la gestione del database tramite Liquibase che è una libreria per la gestione e il versioning di un database [10]. La libreria utilizzata per standardizzare le API REST è Swagger, una libreria che permette di uniformare la documentazione delle API e renderle fruibili [8].
Classi di startup
Come una classica configurazione di Spring Boot, JHipster realizza il package di default inserito da riga di comando (nel nostro caso: it.mokabyte.jhmoka) e crea due classi per lo start dell’applicazione: JhmokaApp e ApplicationWebXml.
JhmokaApp
JhmokaApp è la classe per lo start dell’applicazione come Java stand alone.
@ComponentScan @EnableAutoConfiguration(exclude = { MetricFilterAutoConfiguration.class,MetricRepositoryAutoConfiguration.class }) @EnableConfigurationProperties({ JHipsterProperties.class, LiquibaseProperties.class }) public class JhmokaApp { . . . . . public static void main(String[] args) throws UnknownHostException { SpringApplication app = new SpringApplication(JhmokaApp.class); DefaultProfileUtil.addDefaultProfile(app); Environment env = app.run(args).getEnvironment(); } }
ApplicationWebXml
ApplicationWebXml è classe per lo start dell’applicazione come WAR deployato in un Java EE container.
package it.mokabyte.jhmoka; public class ApplicationWebXml extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { DefaultProfileUtil.addDefaultProfile(application.application()); return application.sources(JhmokaApp.class); } }
Il package domain e repository
Nel package domain, ci sono le entities che saranno utilizzati dall’applicazione.
L’entity su cui si basa jhmoka è l’entity Author di classe it.mokabyte.jhmoka.domain.Author; è l’Entity Bean generato da JHipster a fronte del comando da console [1].
yo jhipster:entity Author package it.mokabyte.jhmoka.domain; . . . /** * A Author. */ @Entity @Table(name = "author") @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public class Author implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; . . . @NotNull @Size(min = 3) @Pattern(regexp = "^\\w+@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}$") @Column(name = "email", nullable = false) private String email; . . . public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public boolean equals(Object o) { . . . } }
La classe AuthorRepository è il relativo Repository Hibernate.
I servizi
Le classi per la logica di business già presenti nell’applicazione di base sono indicate nella figura che segue.
L’interfaccia it.mokabyte.jhmoka.service.AuthorService espone i metodi CRUD (Create, Read, Update, Delete) che saranno implementati dalla classe concreta it.mokabyte.jhmoka.service.impl.AuthorServiceImpl
package it.mokabyte.jhmoka.service; /** * Service Interface for managing Author. */ public interface AuthorService { /** * Save a author. * @param author the entity to save * @return the persisted entity */ Author save(Author author); /** * Get all the authors. * @param pageable the pagination information * @return the list of entities */ Page<Author> findAll(Pageable pageable); /** * Get the "id" author. * * @param id the id of the entity * @return the entity */ Author findOne(Long id); /** * Delete the "id" author. * * @param id the id of the entity */ void delete(Long id); }
I controller rest
Nei package rest ci sono le API implementate tramite i Rest Controller di Spring. Questi sono utilizzati dai controller Angular per ottenere le informazioni di back end dallo strato dei servizi. Il Controller Rest core di jhmoka è it.mokabyte.jhmoka.web.rest.AuthorResource.
Client side
Proseguiamo ora con la descrizione della parte client.
Struttura delle cartelle
JHipster , seguendo il pattern di AngularJS, imposta la struttura delle cartelle client side molto ben organizzata e consente una facile lettura e manutenzione.
Nella cartella root, troviamo già i file utili per la struttura di base di una web application:
- html: layout per gestire il messaggio di pagina non trovata.
- ico: l’icona da associare all’applicazione web.
- txt: il file per definire cosa indicizzare per i motori di ricerca.
- index.html: la pagina iniziale con tutti gli import javascript/css e le configurazioni di base per caricare la GUI secondo il pattern AngularJS. All’interno di questa pagina è presente uno script utile all’abilitazione degli strumenti Google Analytics. Lo script risulta commentato di default.
Oltre ai file sopra citati è presente la seguente struttura di cartelle:
- app: con le sottocartelle e i file per la realizzazione della struttura javascript/html secondo il pattern di AngularJS (approfondiremo meglio nel paragrafo successivo).
- bower_components: la cartella contenente le librerie javascript di terze parti.
- content: con le immagini e i file css.
- i18n: contenente i file di properties nelle varie lingue scelte.
- swagger-ui: la cartella con i file necessari per la renderizzazione della documentazione delle API.
AngularJS
JHipster usa AngularJS come framework JavaScript di riferimento per lo sviluppo della User Interface che è uno dei più famosi framework JavaScript open source utilizzato per la realizzazione di single page application. AngularJS utilizza il pattern MVC all’interno di una pagina web tramite controller e services javascript e invocando i servizi REST di back end [12]. AngularJS è molto famoso nella community web e dispone di una vasta scelta di plugin.
JHipster adotta alcuni dei plugin più famosi già nella configurazione di base; ad esempio Angular UI Router usato per semplificare l’architettura di AngularJS e la gestione della navigazione tra le pagine web [13].
Nell’immagine successiva, un esempio di come avviene il routing dalla homepage alla pagina degli autori, passando dal link Author presente nel menù sotto la voce Entities.
Il link Author è definito all’interno del file navbar.html, aggiornato man mano che aggiungiamo le entities al nostro progetto con la pagina CRUD creata da JHipster. Sul link c’è un attributo dal nome ui-sref che connette il link alle impostazioni di state definite nel file JavaScript degli stati di riferimento all’entità (in questo caso author.state.js).
In questo caso, nel file è definito uno state author e ha i riferimenti di routing, URL di riferimento, eventuali ruoli legati alla pagina, titolo della pagina e molto altro. Soffermiamoci sul routing che instrada al controller Angular di riferimento (AuthorController) e il template da invocare per il rendering delle informazioni (authors.html). Come in un classico pattern MVC dei framework Java più diffusi (p.e.:Spring MVC, Struts) definiamo Controller e View, però al posto di usare una classe Action (o Controller) che prepara i dati da passare a una JSP che li interpreta tramite JSTL o JSP Expression Languages, il Controller è una function JavaScript e la View è una pagina html con attributi sui tag interpretati da AngularJS.
L’AuthorController è il controller di riferimento in questo esempio: la function è già definita con dei parametri in ingresso che rappresentano servizi iniettati dal container 12 (proprio come una classica dependency injection di Spring). Tra i primi comandi che esegue è la funzione loadAll() che invoca il servizio Author e la sua funzione query. Il servizio, iniettato nel controller, è definito nel file author.service.js e implementa una classica angular resource [15] che si connette al RestController Java AuthorResource.java implementato seguendo gli standard REST verbs GET, POST, PUT e DELETE [14]. Il metodo di query invoca /api/authors caricando un array JSON con tutti gli autori presenti su database. A questo punto, il controller salverà nella function onSuccess l’array di dati in una variabile javascript authors.
Una volta eseguita la funzione del controller, AngularJS renderizza la pagina authors.html che, all’interno della table, itera gli authors e realizza una riga della tabella per ognuno di essi, impostando anche i link per le altre pagine da visualizzare di edit, delete e read.
Per concludere è bene notare che JHipster genera, sempre automaticamente, insieme al codice dell’applicazione, una serie di test automatici! I framework di test utilizzati sono: Junit, Cucumber, Karma.js, Gatling e Protractor.
Conclusioni
In questo articolo abbiamo dato una rapida scorsa a cosa viene generato dietro le quinte da JHipster dell’applicazione jhmoka creata e spiegata nello scorso articolo.