Upload
johannes-geppert
View
603
Download
2
Embed Size (px)
Citation preview
Java Web applicationsbased on
and
ApacheCon North America 2016 by ( )Johannes Geppert @jogep
About meApache Member and Struts PMC Member
Software Developer @
Living and working in Leipzig
About Struts2Action based Javaweb frameworkBuilt upon aRequest/ResponsecycleClean architectureEasy to extendwith plugins
Conceptual Overview
Struts 2.5 is released on11 May 2016!
Cleanup and Maintenance!Switch to Java7Increased Security withSMIxworkcore merged intostrutscoreRemoval of deprecatedpluginsDojo PluginCode Behind PluginJSF PluginStruts1 Plugin
Support for bean validation
Now as a (builtin) plugin available
Log4j2 as new Logging Layer
Replacement for Struts2Logging LayerSupport for multiplelogging implementationsBetter performance
First release is available!
Why AngularJS? AngularJS is a structural framework for dynamic web apps.It lets you use HTML as your template language and letsyou extend HTML's syntax to express your application'scomponents clearly and succinctly. Angular's data bindingand dependency injection eliminate much of the code youwould otherwise have to write. And it all happens within the
browser, making it an ideal partner with any servertechnology.
https://docs.angularjs.org/guide/introduction
Google TrendsAngularJS, React, Backbone and ember.js
Blue line is the trend for AngularJS
AngularJS Overview
Quickstart with MavenArchetypes
mvn archetype:generate B \ DgroupId=com.mycompany.mysystem \ DartifactId=myWebApp \ DarchetypeGroupId=org.apache.struts \ DarchetypeArtifactId=struts2archetypeangularjs \ DarchetypeVersion=<CURRENT_STRUTS_VERSION> \ DremoteRepositories=http://struts.apache.org
cd myWebApp mvn jetty:run
Open Browser http://localhost:8080
Live Demo
REST Based Actionswith Struts2 REST Plugin
REST Action MappingHTTP method URI Class.method ParametersGET /order OrderController.index
GET /order/1 OrderController.show id="1"
POST /order OrderController.create
PUT /order/1 OrderController.update id="1"
DELETE /order/1 OrderController.destroy id="1"
Configure the REST PluginAdd the rest plugin to the dependencies
<dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2restplugin</artifactId> <version>$struts2.version</version> </dependency>
Disable restrictToGET default behaviour<constant name="struts.rest.content.restrictToGET" value="false"/>
Create packages for applications<constant name="struts.convention.default.parent.package" value="restangular"/> <package name="restangular" extends="restdefault"> <defaultactionref name="index" /> </package><package name="data" extends="restangular" namespace="/data"> </package>
REST Content Type Handler/order/1 or /order/1.action Dispatcher (e.g. JSP)
/order/1.xml XML Handler
/order/1.json JSON HandlerEasy to build e.g. for CSV resultBuiltin Jackson support for JSON serialization
<bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="jackson" class="org.apache.struts2.rest.handler.JacksonLibHandler"/> <constant name="struts.rest.handlerOverride.json" value="jackson"/>
Live Demo
Exception Handling
Custom ExceptionInterceptor
protected String doIntercept(ActionInvocation actionInvocation) throws Exception try return actionInvocation.invoke(); catch (Exception exception) Map<String, Object> errors = new HashMap<>(); HttpHeaders httpHeaders = new DefaultHttpHeaders() .disableCaching().withStatus(HttpServletResponse.SC_BAD_REQUEST) .renderResult(Action.INPUT); if(exception instanceof SecurityException) errors.put(ACTION_ERROR, "Operation not allowed!"); httpHeaders.setStatus(HttpServletResponse.SC_FORBIDDEN); else errors.put(ACTION_ERROR, exception.getMessage()); return manager.handleResult(actionInvocation.getProxy().getConfig(), httpHeaders, errors);
Extend the DefaultInterceptor Stack
<package name="data" extends="restangular" namespace="/data"> <interceptors> <interceptor name="dataError" class="....ExceptionHandlerInterceptor"/>
<interceptorstack name="dataDefaultStack"> <interceptorref name="dataError"/> <interceptorref name="restDefaultStack"/> </interceptorstack> </interceptors> <defaultinterceptorref name="dataDefaultStack"/> </package>
Dispatch an error eventExtend the generic _request method in DataService$http(req).success(function(data) def.resolve(data); ).error(function(data, code) def.reject(data); if(data.actionError) $rootScope.$emit('dataerror', msg: data.actionError ); );
Listen to error eventse.g in a Controller
$rootScope.$on('dataerror', function(event, alert) console.log(alert.msg); );
Live Demo
Bean Validation
Client and Server sideNew bean validation plugin
Setup bean validation
Add the bean validation plugin to thedependencies
<dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2beanvalidationplugin</artifactId> <version>$struts2.version</version> </dependency>
Specify a validation http status codelike "Not Acceptable"
<! Set validation failure status code > <constant name="struts.rest.validationFailureStatusCode" value="406"/>
Change rest interceptor stackDefault validation interceptor is using the old validationinterceptorCopy the rest default interceptor stackDefine the new one<interceptor name="beanValidation" class="....interceptor.BeanValidationInterceptor"/>
Replace the "validation" reference with "beanValidation"reference in the stack
Live Demo
MultiLanguage Support
Where do we need it?
Frontend Validation Backend
Resource BundlesSplit them up!
<constant name="struts.custom.i18n.resources" value="frontend,validation,exceptions"/>
Sample for validation messages#validation_en.properties validation.order.client = Client name can not be blank validation.order.amount = Order amount needs to be between 10 and 666
#validation_de.properties validation.order.client = Kunden Name darf nicht leer sein validation.order.amount = Anzahl muss zwischen 10 und 666 sein
Language Controllerpublic class LanguageController extends RestActionSupport implements ModelDriven<Map<String, String>>
private Map<String, String> model;
public String index() throws Exception ResourceBundle bundle = getTexts("frontend"); this.model = bundle.keySet().stream() .collect(Collectors.toMap( key > key, key > bundle::getString));
return Action.SUCCESS;
public Map<String, String> getModel() return model;
Setup Angular Translate
(function() 'use strict';
angular .module('app', ['ngRoute', 'ui.bootstrap', 'pascalprecht.translate']); )();
$translateProvider.registerAvailableLanguageKeys(['en', 'de']); $translateProvider.fallbackLanguage('en'); $translateProvider.useUrlLoader('data/language.json', queryParameter: 'request_locale' ); $translateProvider.determinePreferredLanguage();
With translate filter in templates'order.client' | translate
In validation messages@NotBlank(message = "validation.order.client") @Min(value = 10, message = "validation.order.amount") @Max(value = 666, message = "validation.order.amount")
In java codethrow new RuntimeException(getText("exception.not.supported"));
Live Demo
Thank you!https://twitter.com/jogep
Resources
Apache Struts Project https://struts.apache.orgStruts2 Maven Archetypes https://struts.apache.org/docs/struts2mavenarchetypes.htmlStruts2 Examples https://github.com/apache/strutsexamplesAngularJS https://angularjs.orgAngular Translate https://angulartranslate.github.io
AttributionsLeipzig Pictures by
by by
by by
by by
Ruthe Cartoon by by
by by
by by
Johannes GeppertModel in the wind tunnel DLR German Aerospace CenterSky lanterns ( Bhavishya GoelClean Up Allen GoldblattBeans MatthewMatrix pills ThomasThomasShaking Hands Aaron Gilson
ruthe.deLanguage Scramble Eric AndresenWindows Box Perfection Rachel KramerKrakow Door John Finn1949 Ford Coupe flat head V8 engine dave_7Questions Alexander Henning Drachmann