Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Schnelle und leichtgewichtige Anwendungsentwick...

Schnelle und leichtgewichtige Anwendungsentwicklung mit HTML5 und JEE/REST (JUG Fffm)

Präsentiert bei der Java User Group in Frankfurt. Jetzt ein erweitereter Vortrag im Vergleich zur Version in Berlin. Auf Anregung diesmal auch mit Live-Coding.

Alexander Schwartz

June 26, 2013
Tweet

More Decks by Alexander Schwartz

Other Decks in Programming

Transcript

  1. Schnelle und leichtgewichtige Anwendungsentwicklung mit HTML5 und JEE REST Alexander

    Schwartz Java User Group Frankfurt / 26. Juni 2013 1 © msg systems ag, 26. Juni 2013 HTML5 und JEE REST / JUG Frankfurt / Alexander Schwartz
  2. 2 Was ich vorhabe 1. Worum es geht 2. Fachlicher

    Einstieg in das Beispiel 3. JEE Stack für REST auf dem Server 4. HTTP + JavaScript Stack auf dem Client 5. Testen der Anwendung 6. Detaillierung Server-Tests 7. Detaillierung Client-Tests 8. Zusammenfassung © msg systems ag, 26. Juni 2013 HTML5 und JEE REST / JUG Frankfurt / Alexander Schwartz
  3. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 3 Worum es hier geht • HTML als Seitenbeschreibung • CSS für ansprechendes (responsive) Design • Widget z.B. via jQueryUI + Plugins • Clientlogik via JavaScript • Strukturierung mit Javascript MV*-Frameworks (z. B. KnockoutJS) • Modularisierung und Nachladen von Resourcen (z. B. requireJS) • JAX-RS 1.0/2.0 als Teil von JEE 6/7 • Kommunikation via REST/JSON ideal für JavaScript • Persistenz via JPA • Validierung von Daten mit Bean Validation • CDI für Erweiterungspunkte • Integration in Enterprise IT dank vieler Java-Bibliotheken Browser als Client-Plattform JEE REST für serverseitige Services HTML+JavaScript und JEE+REST als Plattform
  4. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 4 Mein Sponsor und Arbeitgeber 1980 gegründet mehr als 4000 Kollegen 8 Branchen 540 Mio € Umsatz 2012 22 Länder 16 deutsche Standorte msg systems ag
  5. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 5 Wer ich bin Alexander Schwartz Lead IT Consultant im GB Travel und Logistics 10 Jahre Java 7 Jahre PL/SQL 7 Jahre Absatzfinanzierung 3,5 Jahre Direktbank 1 Frau 2 Kinder 272 gefundene Geocaches
  6. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 6 User Story: „Nutzer möchte die Sichtung eines Schiffes erfassen, um später sehen zu können, ob auch andere dieses Schiff gesehen haben. Hierzu erfasst er Schifftstyp, Datum, Zeitzone und eine Notiz.“ Die heutige Aufgabe Online-Datenbank für (Raum-)Schiffsichtungen Garantiert kein Bezug zu einem Kundenprojekt! https://github.com/ahus1/rest-samples
  7. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 7 Unser Wegweiser • Darstellung User-Interface • Interaktion mit dem Nutzer V View • Datenhaltung im Client • Bindung an den View M View Model • Kommunikation zwischen Server/Client • Besteht aus server- und clientseitigem Teil C Communication • Fachliche Business-Logik • Greift auf Persistenz zu B Business Services • Datenhaltung und Persistenz P Persistence Eine klare Schichtentrennung hilft als Wegweiser durch die Architektur Client Server
  8. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 8 Was es im Inneren zusammenhält Sichtung Schiffstyp Zeitzone Domain-Model für Struktur hilft Fachabteilung und Entwicklern M C P Client Server
  9. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 9 Wie´s für den Kunden aussieht Mockups klären früh das Maskenlayout und verringern Nacharbeiten P Client Server V
  10. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 10 • IDs werden automatisch generiert • Validierung über Bean-Validation • Basis-Klasse AbstractEntity für Optimistic Locking Los geht‘s – die erste Entität Standard-JPA-Entität für „Zeitzone“ P Client Server @Entity public class Timezone extends AbstractEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long timezoneId; @NotEmpty private String timezoneName; … } @MappedSuperclass public class AbstractEntity { @Version private Integer version; … }
  11. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 11 • Übersetzbares Element als Entität Translation • CascadeType.ALL für gemeinsame Speicherung • Orphan Removal für Housekeeping • Translation enthält eine Map mit Übersetzungen Entität mit Übersetzung Komplexe Entität „Schiffstyp“ P Client Server @Entity public class Vessel extends AbstractEntity { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long vesselId; @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "vesselName") private Translation vesselName; … } @Entity public class Translation extends AbstractEntity { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long translationId; @ElementCollection @CollectionTable(name = "Text", joinColumns = @JoinColumn(name = "translationId")) @MapKeyColumn(name = "textLanguage") @Column(name = "textString") private Map<Locale, String> texts; … }
  12. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 12 Vorsicht beim (Daten-Model) Bau Abbildung fachlicher Datentypen auf technische Datentypen Fachlich Java JSON JavaScript Zeichenkette String String String Ganzzahl Long Number Number Dezimalbruch BigDecimal String * String * Datum LocalDateTime String ** String ** * JavaScript Number-Type unterstützt nur Fließkommazahlen, aber keine Dezimalzahlen. Es können dadurch Rundungsdifferenzen auftreten, die fachlich nicht gewünscht sind. Daher Fallback auf String. ** „Standard“ bei JSON ist für Datumsangaben Sekunden seit 1970 und Zeit UTC. LocalDateTime lässt sich so nicht abbilden; Date in JavaScript wird in der Zeitzone des Browser dargestellt => keine Kontrolle durch Anwendung möglich möglich. Daher Fallback auf Datum als String im ISO-Format M C P Client Server
  13. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 13 • ManyToOne-Relation für Selectbox Vessel – ohne Cascade! • Joda-Elemente für Zeiten; usertype für Joda-Persistenz Datum in JPA Komplexe Entität „Sichtung“ P Client Server @Entity public class Sighting extends AbstractEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long sightingId; private String sightingMemo; @ManyToOne @JoinColumn(name = "vesselId") private Vessel vessel; @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTimeZoneAsString") private DateTimeZone sightingTimezone; @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime") private LocalDateTime sightingDate; … }
  14. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 14 • DefaultRestEndpoint liefert CRUD Funktionalität • Genug „Platz“ im Endpoint für spezifische Funktionalität (z.B. Aufruf von Business Services) • Sicherstellung „Don‘t Repeat Yourself“ Erster REST Endpoint Java-Generics sind gut! @Stateless @Path("/timezone") public class TimezoneEndpoint extends DefaultRestEndpoint<Timezone> { } Client Server C @Produces({ "application/json", "text/xml" }) @Consumes({ "application/json", "text/xml" }) public abstract class DefaultRestEndpoint<ENTITY> { < … 500 Zeilen Generics, Reflection, JPA Metamodel, JavaDoc … Einblick auf der nächsten Folie … > }
  15. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 15 Blick durch Schlüsselloch in DefaultRestEndpoint Client Server C public abstract class DefaultRestEndpoint<ENTITY> { … @GET @Path("/{id:.+}") public Response findById(@PathParam("id") String id, @Context Request request) { ENTITY result = em.find(getEntityClass(), constructPK(id)); if (result == null) { throw new EntityNotFoundException(); } pullByJsonView(result, getExtendedView()); return Response.ok(result).build(); } … public abstract class DefaultRestEndpoint<ENTITY> { … @POST @Path("") public Response add(ENTITY entity) { bindReadOnlyEntities(entity); em.persist(entity); em.flush(); UriBuilder locationBuilder = uriInfo.getBaseUriBuilder(); locationBuilder.path(this.getClass()); PersistenceUnitUtil puu = em.getEntityManagerFactory() .getPersistenceUnitUtil(); URI childLocation = locationBuilder.path("{id}").build( puu.getIdentifier(entity)); return Response.status(Response.Status.CREATED).location(childLocation) .build(); } … public abstract class DefaultRestEndpoint<ENTITY> { …. @POST @Path("/qbe") public List<ENTITY> queryByExample(ENTITY entity) { Session session = (Session) em.getDelegate(); Example example = Example.create(entity).enableLike(MatchMode.ANYWHERE) .ignoreCase(); Criteria criteria = session.createCriteria(entity.getClass()).add(example); addSubCriteria(criteria, entity); if (criteria.list().isEmpty()) { return null; } else { pullByJsonView(criteria.list(), getExtendedView()); return criteria.list(); } } …
  16. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 16 Object Graph Traversal Client Server C @Stateless @Path("/sighting") public class SightingEndpoint extends DefaultRestEndpoint<Sighting> { @Override protected Class<?> getExtendedView() { return Sighting.Extended.class; } @Override @GET @Path("/{id:.+}") @JsonView(Sighting.Extended.class) public Response findById(@PathParam("id") String id, @Context Request request) { return super.findById(id, request); } … } Mit @JsonView-Annotationen kann die Sichtbarkeit eingeschränkt werden public abstract class DefaultRestEndpoint<ENTITY> { @GET @JsonView(ListView.class) public Response listAll() { … } public class Sighting extends AbstractEntity { … @JsonView(Extended.class) public String getSightingMemo() { return sightingMemo; } … }
  17. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 17 • Fachliche Überlegung: kein sightingMemo in der Übersicht notwendig • Technische Überlegung: kein version in der Übersicht notwendig • ABER: @JsonView gibt‘s nur bei Jackson (nicht JEE Standard) Object Graph Traversal Output Client Server C GET http://localhost:8080/rest-samples/rest/sighting [{"sightingId":1, "vessel":{"vesselId":1,"vesselName":"Klingonischer Jäger"}, "sightingTimezone":"Europe/Berlin", "sightingDate":"2013-04-11T11:00:00.000"}, {"sightingId":2, "vessel":{"vesselId":1,"vesselName":"Klingonischer Jäger"}, "sightingTimezone":"Europe/London", "sightingDate":"2013-04-11T22:00:00.000"}] In der Liste sind weniger Attribute angezeigt, in der Einzelansicht mehr GET http://localhost:8080/rest-samples/rest/sighting/-1 {"version":1, "sightingId":1, "sightingMemo":"unheimlich!", "vessel":{"version":0,"vesselId":-1,"vesselName":"Klingonischer Jäger"}, "sightingTimezone":"Europe/Berlin", "sightingDate":"2013-04-11T11:00:00.000"}
  18. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 18 Erweiterungspunkt: Custom Serialisierer Client Server C public class DateTimeZoneDeserializer extends JsonDeserializer<DateTimeZone> { @Override public DateTimeZone deserialize(JsonParser jparse, DeserializationContext context) throws IOException { String text = jparse.getText(); if (text == null || text.trim().length() == 0) { return null; } else { return DateTimeZone.forID(text); } } } DateTimeZone serialisieren (da kein Standardtyp) public class DateTimeZoneSerializer extends JsonSerializer<DateTimeZone> { @Override public void serialize(DateTimeZone value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeString(value.getID()); } } • Ist kein Standardtyp • Eigener Serialisierer kann eingebunden werden
  19. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 19 • Serialiserer für DateTimeZone und BigDecimal registrieren Erweiterungspunkt: Custom Serialisierer Client Server C @Provider public class CustomObjectMapper implements ContextResolver<ObjectMapper> { @Override public ObjectMapper getContext(Class<?> type) { final ObjectMapper result = new ObjectMapper(); SimpleModule module = new SimpleModule(getClass().getName(), new Version(1, 0, 0, null)) .addDeserializer(BigDecimal.class, new BigDecimalAmountDeserializer()) .addSerializer(BigDecimal.class, new BigDecimalAmountSerializer()) .addDeserializer(DateTimeZone.class, new DateTimeZoneDeserializer()) .addSerializer(DateTimeZone.class, new DateTimeZoneSerializer()); result.registerModule(module); result.configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false); return result; } } Registrieren der Serialisierer / De-Serialisierer
  20. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 20 Exception Handler Client Server C @Provider public class EntityNotFoundExceptionMapper implements ExceptionMapper<EntityNotFoundException> { @Inject private Localizer localizer; @Override public Response toResponse(EntityNotFoundException exception) { Map<String, String> responseObj = new HashMap<String, String>(); responseObj.put("general", localizer.localize("error.entityNotFound")); return Response.status(Response.Status.NOT_FOUND).entity(responseObj) .build(); } } Für jede Exception kann (muss) ein eigener Handler definiert werden public class Localizer { @Inject private HttpServletRequest httpServletRequest; public String localize(String key) { try { ResourceBundle rb = ResourceBundle.getBundle("messages", httpServletRequest.getLocale()); return rb.getString(key); } catch (MissingResourceException e) { return "{" + key + "}"; } } }
  21. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 21 Validierung von Daten Client Server C Bean Validation plus Exception-Handler #ValidationMessages_de.properties sighting.memo.minLength=Muss mindestens {min} Zeichen lang sein @Entity public class Sighting extends AbstractEntity { … @NotNull @Size(min = 2, message = "{sighting.memo.minLength}") private String sightingMemo; … } @Provider public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> { @Override public Response toResponse(ConstraintViolationException exception) { Map<String, Object> responseObj = new HashMap<String, Object>(); for (ConstraintViolation<?> violation : exception.getConstraintViolations()) { // … < 40 Zeilen Code > … } return Response.status(Response.Status.BAD_REQUEST).entity(responseObj) .build(); } P
  22. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 22 • Querschnittsfunktionen lassen sich einfach von domänenspezifischen Elementen trennen • APIs und Erweiterungspunkte für spezifische Implementierungen sind vorhanden • JSON Serialisierung ist nicht in JEE 6.0 standardisiert • Durch Generics, JPA Metamodel und Reflection gelingt es, das DRY Prinzip durchzuhalten JEE Zwischenfazit Client Server C JEE REST: JAX-RS P
  23. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 23 Anforderungen: • HTML für den View • View-Model für Datenhaltung • Bi-direktionales Datenbinding View / View-Model • Modularisierung von JavaScript-Bibliotheken, aber auch Templates und JavaScript-Code für Usecases der Anwendung • Stateful-URLs innerhalb der Anwendung; Vor-/Zurück-Navigation und direkter Einsprung Einstieg HTML Client Server HTML Single Page Apps – aber bitte strukturiert und mit Modularisierung V M
  24. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 24 Beispiel: Eingabefelder mit Label und Eingabefeld HTML im View Client Server CSS-Frameworks wie Bootstrap erlauben schlankes HTML V <form class="form-horizontal" > <fieldset> <legend>Zeitzone bearbeiten</legend> <div class="row-fluid"> <div class="control-group"> <label class="control-label" for="bezeichnung">Name</label> <div class="controls"> <input type="text" id="timezoneName" class="input-large" /> </div> </div> </div> </fieldset> </form>
  25. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 25 • Reduktion der Information • Alternative Anordnung der Information HTML im View Client Server Responsive Design abhängig vom Endgerät/Bildschrimbreite V
  26. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 26 Umgesetzt mit KnockoutJS View-Model Client Server Ein Menü anzeigen – Trennung View vom View Model V // menu.js var Menu = function() { // Data var self = this; self.folders = ko.observableArray([ { name : 'Schiffstypen', link : '#/vessel/main' }, { name : 'Sichtungen', link : '#/sighting/main' }, { name : 'Zeitzonen', link : '#/timezone/main' } ]); self.folder = ko.observable(); } <!-- index.html --> <ul class="nav" data-bind="foreach: menu.folders"> <li data-bind="css: { active: $data.link == $parent.menu.folder() }, attr: {id: $data.link} "><a data-bind="text: $data.name, attr: {href: $data.link}"></a></li> </ul> M
  27. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 27 Eine Texteingabe im Input-Feld aktualisiert das Modell – eine Änderung im Modell aktualisiert den View View-Model Client Server Bidirektionales Binding V <!-- vessel.js --> self.deleteLanguage = function(language) { self.vessel().vesselName.remove(language); }; M <!-- vessel.html --> <a class="btn btn-small“ data-bind="click: $parents[2].deleteLanguage"> <i class="icon-trash"></i> </a>
  28. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 28 Eine Texteingabe im Input-Feld aktualisiert das Modell – eine Änderung im Modell aktualisiert den View Berechnete Elemente für Internationalisierung Client Server Wenn sich die Sprache ändert, ändern sich alle Übersetzungen V M /* knockout.i18n.js */ ko.i18n = function(key) { return ko.computed(function() { if (ko.language() != null) { return i18next.t(key, { lng : ko.language() }); } else { return ""; } }, key); }; <!– vessel.html --> <button type="button" id="save" class="btn- primary btn" data-bind="click: $parent.saveVessel, text: ko.i18n('glb.save')"></button> <a href="#" class="btn" data-bind="click: $parent.goToMain, text: ko.i18n('glb.cancel')"></a>
  29. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 29 Damit kann an jedem Use Case noch unabhängiger gearbeitet werden Modularisierte Resource-Dateien Client Server Jeder Use Case kann eine eigene Datei bekommen V <!– vessel.html --> <label class="control-label" for="bezeichnung" data-bind="text: ko.i18n('vessel:vessel.name')"></label> … <button type="button" id="save" class="btn-primary btn" data-bind="click: $parent.saveVessel, text: ko.i18n('glb.save')"></button> <a href="#" class="btn" data-bind="click: $parent.goToMain, text: ko.i18n('glb.cancel')"></a>
  30. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 30 • Jedes Modul erhält einen „Header“ mit Abhängigkeiten • Für 3rd-Party-Bibliotheken werden im der Startprozedur Abhängigkeiten definiert • requireJS sorgt für parallelen Download der JS-Module Modularisierung (Model) Client Server Modularisierung der Anwendung (Model) mit requireJS V <!-- vessel.js --> define( [ 'knockout', 'jquery', 'mapping', 'hasher', 'crossroads', 'menu' ], function(ko, $, mapping, hasher, crossroads, menu) { // logik… }); M //main.js require.config({ shim : { jquery : { exports : "jQuery" }, knockout : { deps : [ 'jquery' ] }, 'knockout.validation' : { deps : [ 'knockout' ] },… <!– index.html --> <script data-main="js/main" src="js/libs/require.js"></script>
  31. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 31 Modularisierung (Usecases) Client Server Modularisierung der Use-Cases V M übergreifende Module Bibliotheken ein Template pro Use Case eine JS Datei pro Use Case
  32. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 32 • HTML5 unterstützt das „History“ API, bei dem URLs sich ändern können, auch wenn die Seite gleich bleibt • Zusatzinformationen zu einer Seite können per JavaScript in der Browser-Historie abgelegt werden Ohne History-API: • Workaround: URL hinter dem Hash-Tag http://localhost:8080/rest-samples/#/vessel/edit/1 http://localhost:8080/rest-samples/#/vessel/main http://localhost:8080/rest-samples/#/timezone/edit/1 • Unterstützt z.B. durch Crossroads.js Stateful URLs Client Server URLs für den direkten Einstieg – und Vor-/Zurücknavigation V crossroads.addRoute(/vessel\/edit\/(.+)$/, function(id) { $.get("rest/vessel/" + id, function(data) { self.vessel(mapping.fromJS(toViewModel(data))); self.vesselList(null); }); });
  33. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 33 • Laden eines Elements • Das Model kann ggf. für den View punktuell transformiert werden, z.B. HashMap als Array REST-Anbindung in JavaScript Client Server Das Model im Backend kann weitgehend beibehalten werden $.get("rest/vessel/" + id, function(data) { self.vessel(mapping.fromJS(toViewModel(data))); self.vesselList(null); }); function toViewModel(data) { data = jQuery.extend(true, {}, data); var text = []; $.each(data.vesselName, function(key, value) { text.push({ textLanguage : key, textString : value }); }); data.vesselName = text; return data; } C M M
  34. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 34 • Querschnittsfunktionen lassen sich einfach von domänenspezifischen Elementen trennen • APIs und Erweiterungspunkte für spezifische Implementierungen sind vorhanden • Die Zahl der Bibliotheken und APIs ist deutlich größer als im JEE Bereich • Ist der Rahmen vorbereitet, können einfach neue Use Cases dazuentwickelt werden (je eine JS + HTML Datei pro Use Case) • Code-Änderungen sind nach einem Browser-Refresh sofort sichtbar HTML/JS Zwischenfazit Client Server HTML + JavaScript können strukturiert werden! V M C
  35. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 35 Automatisiertes Testen Client Server Verschiedene Testframeworks decken verschiedene Bereiche ab V M C B P Jasmine, Sinon Selenium IDE Selenium RC Junit Arquillian Arquillian + REST- assured Mock oder echt? Mock oder echt? geringe Komplexität hohe Komplexität
  36. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 36 Arquillian: • Automatisiertes Deployment von Teilen der Server-Anwendung (ggf. ergänzt um Mock-Komponenten) REST-assured: • Testen der REST-services mit Fluent API Arquillian + REST assured Client Server Test-Anwendungen automatisiert packen und laufen lassen @RunWith(Arquillian.class) public class MesseEndpointTest { @Deployment public static WebArchive createArchiveAndDeploy() { WebArchive war = ShrinkWrap.create(WebArchive.class, "arquillian-rest-demo.war"); war.addPackages(…); war.addAsLibraries(…); return war; } @Test public void testReturnFilledList() throws Exception { Response r = given().log().all() .contentType(ContentType.JSON).expect() .body("[0].meBezeichnung", equalTo("Vessel 1")) .statusCode(Status.OK.getStatusCode()).when() .get("/rest-samples/rest/vessel"); assertThat("only one element exists", r.body().jsonPath().getList("").size(), equalTo(1)); } B P C
  37. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 37 Kleine Tests: • Unit-Tests für JavaScript-Code-Bibliotheken • Ergänzt durch HTML-Fragmente • DOM-Interaktion möglich durch jQuery Integration Große Tests: • Tests der vollständigen Use Cases Jasmine BDD Testframework Client Server Anforderungen beschreiben und Tests automatisieren auf dem Client V M describe("Manage Vessels", function() { it("shows a list of two vessels at the start", function() { expect($("#vesselList")).toBeVisible(); expect($("#vesselList > tbody > tr")[0]).toContainHtml("Vessel 1"); expect($("#vesselList > tbody > tr")[1]).toContainHtml("Vessel 2"); }); });
  38. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 38 Frontend-Entwickler ist unabhängig vom Backend – und kann selber Testen Sinon.JS für Mocks Client Server Backend kann via JavaScript simuliert werden describe("Manage Vessels", function() { beforeEach(function() { server = sinon.fakeServer.create(); server.respondWith("GET", "rest/vessel", [ 200, { "Content-Type" : "application/json" }, vesselList ]); server.respondWith("DELETE", "rest/vessel/1", [ 204, null, "" ]); server.respondWith("GET", "rest/vessel/1", [ 200, { "Content-Type" : "application/json" }, vessel ]); afterEach(function() { // disable fake server server.restore(); }); ); C
  39. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 39 • 7 Tests • 760 ms Gesamtlaufzeit • Cross-Browser • Unabhängig vom Backend • Dokumentation aus fachlicher Sicht • Beliebige Schachtelung • Chrome etwas schneller als Firefox Jasmine ist schnell und einfach Client Server Ein Browser-Reload lässt die Tests erneut laufen C V M
  40. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 40 • Integration in CI Server möglich • Jasmine Tests erstellen XML entsprechend JUnit (für Jenkins, Eclipse et al) $ phantomjs phantomjs-testrunner.js \ http://localhost:8080/rest-samples/?spec= Testen in der Dunkelverarbeitung Client Server Mit PhantomJS auf der Kommandozeile testen C V M
  41. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 41 • Verbinden mit einer laufenden Browser-Instanz • Breakpoint setzen, Variablen sehen • Hot Code Replacement Debugging mit Chome Client Server Wie Java und ein bisschen mehr? V M
  42. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 42 Debugging mit Chome
  43. <!DOCTYPE html> <html lang="en" manifest="cache.appcache"> ... © msg systems ag,

    26. Juni 2013 HTML5 und JEE REST / JUG Frankfurt / Alexander Schwartz 43 Application Cache Client Server Resourcen werden vorab geladen und über eine Datei validiert V # cache.appcache CACHE: /rest-samples/index.html /rest-samples/js/main.js /rest-samples/js/libs/jquery.js /rest-samples/css/libs/bootstrap.css … NETWORK: * # hash:a0d1f5…eedd • Der Browser erfährt, welche Resourcen er vorab laden soll • Daten werden lokal gespeichert • Validierung via Manifest, ob Resourcen erneut geladen werden sollen (ggf. erst aktivieren, wenn Entwicklung abgeschlossen ist) M
  44. © msg systems ag, 26. Juni 2013 HTML5 und JEE

    REST / JUG Frankfurt / Alexander Schwartz 44 • Backend und Frontend können fachliche Komponenten und technische Komponenten von einander getrennt werden • Steht der Rahmen, so können Entwickler an unabhängig an verschiedenen Use Cases arbeiten • Steht der Rahmen, so können Entwickler mit geringen Vorkenntnissen in die Entwicklung einsteigen • Frontend und Backend können separat entwickelt werden • HTML+JS bietet schnelle Turnaround-Zeiten in der Entwicklung • JEE REST bietet Integration in Enterprise IT und stabile Laufzeitumgebung • Sowohl Frontend als auch Backend können einfach skalieren • Durch neue HTML5 Features wie Application Cache kann die Skalierung weiter erhöht werden Zusammenfassung Client Server JEE REST Backend ergänzt sich mit HTML/JS Frontend V M C B P
  45. www.msg-systems.com Vielen Dank für Ihre Aufmerksamkeit msg systems ag Mergenthalerallee

    73 - 75 65760 Eschborn Telefon: +49 (171) 5 62 57 67 E-Mail: [email protected] www.msg-systems.com 45 HTML5 und JEE REST / JUG Frankfurt / Alexander Schwartz © msg systems ag, 26. Juni 2013