testdrive angularjs with spring 4

40
© 2014 INFINIT GmbH Dr. Oliver Wahlen oliver.wahlen@infinit-group.de September 2014 Testdrive AngularJS with Spring 4 1

Upload: oliver-wahlen

Post on 12-Jun-2015

5.684 views

Category:

Software


5 download

DESCRIPTION

A simple application called cvdb is used to illustrate best practices in combining AngularJS as a client browser technology with a Spring based Java server. The server architecture utilizes the new Spring Boot module that was introduced with Spring 4 together with other Spring modules like Spring Data, Spring Security, Spring MVC. QueryDSL is used to access a H2 in memory database.

TRANSCRIPT

Page 1: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH

Dr. Oliver [email protected]

!

September 2014

TestdriveAngularJS with Spring 4

1

Page 2: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 2

MotivationINFINIT creates software products for customersSpecialized on Java server and mobile technologyHardware & Software are constantly improving at all levelsMarket success of fixed price projects = Efficiency + Quality

We are constantly improving our processes and tools

It’s Fun!!!

Page 3: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 3

Challenges of JavaEE projectsusing EJB and JSF…

fixed component set (xxxFaces) is difficult to customizeslow due to JSF lifecycle / application statehard to integrate with pure Javascriptdoes not scale because sessions are hugeno integrated testing concept: views, unit, integration, functionvariety of IoC frameworks with different constraints (JSF, EJB, CDI)slow development roundtrips(no hot reloading, monolithic App Server)

Page 4: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 4

Challenges of JavaScript projectspure jQuery is no good idea:

no structure by convention: how to stay clean?no test support

what events are bound to what components?maintaining high quality with many developers is difficultJavascript is becoming more important in mobile apps

Page 5: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 5

Motivation for AngularJSUI experience of Single Page ApplicationsElegant programmers model based on JavaScript:Model View Whatever patternDependency InjectionModularization using modules, directivesTesting supportPopularity and community support

Page 6: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 6

What is new in Spring 4New project Spring Boot: Convention over ConfigurationIntegrates with Spring Loaded: reloading classes and resourcesApproved, consistent and complete tool chain:

Spring Core: Dependency Injection,Transaction Mgmt, testing, etc.Spring Data: Consistent approachto access data stores, QuerydslSpring Security … many more

Page 7: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 7

The CV Database Application

Source Code: https://bitbucket.org/infinit-group/cvdbDemo App: http://cvdb.elasticbeanstalk.com/

Application is as responsive as a desktop appCV Database is just complex enough for real-world template

Page 8: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 8

Software Architecture

Page 9: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 9

Client Tooling: JavaScriptnpm Node Packet Manager installs NodeJS packages Yeoman Code Generator generates AngularJS project skeleton from shell Grunt build tool executes tasks defined in JSON file (similar to ant) Bower dependency resolver fetches dependent JS components from Repos (similar to ivy)Karma test driver runs JS tests in JS engine (e.g. phantomjs, Browser with plugin) Jasmine testing Framework tests are written in Jasmine format (similar to Spock) JSHint JavaScript validator ensures good JavaScript code style (IDE integration)

JavaScript uses its own optimized toolchain! -> Bad idea: use java tools like maven, junit, …

Page 10: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 10

Server Tooling: Javagradle build tool incl. dependency resolver, test driver gradle grunt plugin maps grunt tasks onto gradle tasks spring-boot gradle plugin adds Spring Boot specific tasks Spring Boot Convention over Configuration Framework Embedded Tomcat servlet container managed by Spring Boot

Gradle is used as master build system: orchestrates grunt and builds Java

Page 11: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 11

Overall Project Structurecvdb-client separate project for AngularJS clientcvdb-serverSpring based sever code that includes client WARdockeroptionally packaging for Elastic Beanstalkslidesthese slides

Page 12: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 12

Client Side - AngularJS Partviews html fragments with AngularJS tags controllers two way binding view elements, actions, routingservices singletons for business logic, DAO layerinterceptors for request&responses (e.g. Security)directives componentization, encapsulation of DOM manipulationstests techniques to test all components other images, css, fonts, etc.

Page 13: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 13

Enough Theory! Where is the Code?

Note: For education the following code examples are simplifications taken from https://bitbucket.org/infinit-group/cvdb

Page 14: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 14

The entry page: index.html<!doctype html>! <!-- Link body tag with AngularJS module (see app.js) -->! <body ng-app="cvdbClientApp">!! <!-- include html fragments -->! <div ng-include="'views/header.html'"></div>!! <!-- define main view used by routing (usage of $routeProvider)-->! <div class="container" ng-view=""></div>!! <!-- build:js script tags generated by grunt -->! <!-- external scripts retrieved by bower -->! <script src="bower_components/angular/angular.js"></script>! <!-- script files of the application -->! <script src="scripts/app.js"></script>! <script src="scripts/controllers/headercontroller.js"></script>! <!-- endbuild -->! </body>!</html> Using AngularJS to build a single page application

Page 15: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 15

Central AngularJS Configuration: app.js// Definition of dependant modules!angular.module('cvdbClientApp', [! 'ngRoute', // makes $routeProvider injectable! 'ui.bootstrap'!])! .config(['$httpProvider', '$routeProvider', function ($httpProvider, $routeProvider) {! // Configuration phase - only providers injectable!! // Spring security will respond with a "WWW-Authenticate" header! // if the "X-Request-With" header is not sent in the request.! // This will in turn trigger a login popup in the browser.! $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';!! $routeProvider! .otherwise({! // default view to be used if no other URL matcher fits! redirectTo: '/resume/list'! });! }])! .run(function () {! // bootstrap the application - only instances are injectable! /* global FastClick: false */! FastClick.attach(document.body);! });

Page 16: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 16

AngularJS Controller: ResumeController.jsangular.module('cvdbClientApp')! .config(['$routeProvider', function ($routeProvider) {! $routeProvider! .when('/resume/list', {! templateUrl: '/views/resume/list.html',! controller: 'ResumeListController'! })! }])! .controller('ResumeListController', ['$scope', '$location', 'ResumeService', function ($scope, $location, ResumeService) {!! // Actions live in the $scope hierarchy: one $scope per Controller! $scope.onRowClicked = function (row) {! // change browser URL, active Controller and view! $location.path('/resume/show/' + row.id);! };!! // ResumeService is used to load rows asynchronously! ResumeService.list().$promise.then(! // 'then' is called back when received response! function (result) {! // 'mode' is bindable from view! $scope.model = result;! });! };! }]);

Routing URLs to Controllers

Page 17: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 17

Associated HTML template: list.html<h2>Resumes</h2>!<p>! <!-- binding a button to a create action -->! <button class="btn btn-primary" ng-click="create()">! <i class="fa fa-plus fa-lg"></i> Add Resume! </button>!</p>!!<!-- table is rendered by custom directive -->!<!-- 'content' is a JSON element from the server response -->!<my-table rows="model.content" on-row-clicked="onRowClicked(row)">!</my-table>

Page 18: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 18

Custom Directive for tables: mytable.jsangular.module('cvdbClientApp')! .directive('myTable', function () {! return {! templateUrl: '/views/directives/mytable.html',! restrict: 'E', // directive applies to a html element! scope: {! rows: '=', // bind row attribute to directive's $scope! onRowClicked: '&' // bind attribute to function reference! },! controller: ['$scope', function ($scope) {! // called on click in row passing the row as parameter! $scope._onRowClicked = function (row) {! $scope.onRowClicked({row: row});! };! }]! };! });

Page 19: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 19

Directive’s HTML template: mytable.html<table>! <!-- iterate over rows and colums of directive's scope -->! <tr ng-repeat="row in rows" ng-click="_onRowClicked(row)">! <td ng-repeat="column in [0,1]">! {{row[column]}}! </td>! </tr>!</table>

Page 20: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 20

Consuming Server Data: resumeservice.jsangular.module('cvdbClientApp')! .service('ResumeService', ['$resource', function ($resource) {! this._apiUrl = '/api/resume'; // define the REST URL! this._apiUrlId = this._apiUrl + '/:id';!! this.list = function () {! // $resource encapsulates asynchronous REST calls! var resource = $resource(this._apiUrl);! return resource.get({page: 0, size: 1000});! };!! this.get = function (id) {! return $resource(this._apiUrlId).get({id: id});! };!! this.remove = function (id) {! return $resource(this._apiUrlId).delete({id: id});! };!! this.save = function (resume) {! return $resource(this._apiUrl).save(resume);! };! }]);

Page 21: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 21

Authentication: responseinterceptor.jsangular.module('cvdbClientApp')! .factory('responseInterceptor', ['$q', '$location', function($q, $location) {! return {! responseError: function (rejection) {! if (rejection.status === 401) {! $location.path('/auth/login');! } else {! console.log('Error processing request: '! + rejection.statusText! + ' [' + rejection.status + ']');! }! return $q.reject(rejection);! }! };! }])! .config(['$httpProvider', function ($httpProvider) {! $httpProvider.interceptors.push('responseInterceptor');! }]);

Page 22: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 22

Server Side - Spring Partconfig Config Beans with Spring Configurers controller Spring MVC controllers domain folder for JPA-Entities repositories Spring Data repositories services Spring Beans with business logic utils.securityUtility classes for Spring Security

Page 23: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 23

Spring Configuration without XML@Configuration // annotation for configuration Bean!public class MyBeanDefinitions {! @Bean // annotation for bean definition! public PasswordEncoder passwordEncoder() {! // Method name = Bean name! // Method type = Bean type! // Spring calls the method to create the bean! return new StandardPasswordEncoder();! }!};!!

@Component // annotation for a Spring Bean!public class MyBean {! @Autowired // Inject a Spring Bean! private PasswordEncoder passwordEncoder;!}

Page 24: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 24

Spring Boot Configurations

@EnableAutoConfiguration // Enable Magic!@Controller // Define MVC Controller!public class SampleController {! @RequestMapping("/")! @ResponseBody! String home() {! return "Hello World!";! }!!

public static void main(String[] args) throws Exception {! SpringApplication.run(SampleController.class, args);! }!}

An extreme simple but working Spring MVC application!

Page 25: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 25

@EnableAutoConfiguration

Our Experience: Using Convention over Configuration you can create applications efficiently.But you have to know the Conventions!

/**!Enable auto-configuration of the Spring Application Context,!attempting to guess and configure beans that you are likely to need.!Auto-configuration classes are usually applied based on your!classpath and what beans you have defined.!**/

Page 26: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 26

Spring’s Convention Implementation [1]@Configuration!// ...!public class WebMvcAutoConfiguration {! // ...! @Bean! @ConditionalOnMissingBean(RequestContextListener.class)! public RequestContextListener requestContextListener() {! return new RequestContextListener();! }! // ...!}

Conditional configurations: Sprint Boot Beans are only usedif Bean with that class is not yet contained in BeanFactory.

Page 27: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 27

Spring’s Convention Implementation [2]@Configuration!public class Security extends WebSecurityConfigurerAdapter {! // ...! @Override // Override standard configurations! protected void configure(final HttpSecurity http)! throws Exception {! http! .httpBasic() // basic authentication shall be used! http! .rememberMe(); // rememberMe cookies shall be honoured! http! .csrf().disable() // disable XSS checks with static pages! .authorizeRequests() // define request authorization! // admins can monitor! .antMatchers("/monitoring/**").hasRole("ADMIN")! // API calls must be authenticated! .antMatchers("/api/**").authenticated()! // static content is allowed for anyone - use a CDN!! .anyRequest().permitAll()! }! // ...!}

Security Configurers based on @Override

Page 28: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 28

Spring Data

"Makes it easy to use new data access technologies, such as non-relational databases"

Spring Data JPA: part of the larger Spring Data family […] makes it easy to implement JPA based repositories.

Page 29: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 29

JPA Repositoriespublic interface UserRepository! extends PagingAndSortingRepository<User, Long> {!! User findByUsername(String username);!}!!// Spring Code below this line:!public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {!! Iterable<T> findAll(Sort sort);!! Page<T> findAll(Pageable pageable);!}!!public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {! <S extends T> S save(S entity); // save the entity! T findOne(ID id); // lookup the entity! void delete(T entity); // delete the entity! // and many other methods...!}

Define a custom interface and Spring creates the DAO implementation

Page 30: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 30

Implementing Custom DAO Methods// create a custom interface for the custom DAO method!public interface ResumeRepositoryCustom {! // custom DAO method! Page<Resume> search(String search, Pageable pageable);!}!!// create an implementation class ending with "Impl"!public class ResumeRepositoryImpl! implements ResumeRepositoryCustom {! @Override! public Page<Resume> search(String search, Pageable pageable) {! // perform custom query and return result page! // removed for simplicity! }!}!!// extend Spring Repository and custom implementation!public interface ResumeRepository! extends PagingAndSortingRepository<Resume, Long>,! ResumeRepositoryCustom {!}

Page 31: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 31

QueryDSL for SQL QueriesMotivation: JPA has some significant disadvantages:

JPQL String concatenation leads to mess:no Compiler or IDE-SupportCriteriaBuilder is unreadable for queries of relevant sizeNative queries are way more powerful than JPA queries

QueryDSL is a DB-Query Technology that is:Type Safe (in contrast to String concatenation)Readable (in contrast to CriteriaBuilder API)Supports for JPA queries and native queries

Page 32: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 32

QueryDSL Example// Query using JPA EntityManager!JPQLQuery query = new JPAQuery (entityManager);!cats = query.from(cat)! .innerJoin(cat.mate, mate)! .orderBy(cat.name.asc())! .list(cat);!!// Query and project into DTO!query = new JPASQLQuery(entityManager, templates);!List<CatDTO> catDTOs = query.from(cat)! .orderBy(cat.name.asc())! .list(EConstructor.create(CatDTO.class, cat.id, cat.name));!!// Query in SQL, but project as entity!query = new JPASQLQuery(entityManager, templates);!cats = query.from(cat)! .innerJoin(mate).on(cat.mateId.eq(mate.id))! .where(cat.dtype.eq("Cat"), mate.dtype.eq("Cat"))! .list(catEntity);

QueryDSL allows: type save queriescomplex subselectsreturning entities or DTOs

Page 33: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 33

QueryDSL DrawbackSourcecode Generator is Needed:

generated Java Source Code contains Entity metadataQueryDSL uses the Java-Compiler with an JPAAnnotationProcessor to perform thiscvdb contains a querdsl.gradle file to illustrate the usage.

Page 34: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 34

QueryDSL Integration with Spring// Implement Custom Query!public interface ResumeRepositoryCustom {! Page<Resume> search(String search, Pageable pageable);!}!!// Use QueryDsl!public class ResumeRepositoryImpl implements ResumeRepositoryCustom {! @Autowired! private ResumeRepository resumeRepository;!! @Override! public Page<Resume> search(String search, Pageable pageable) {! QResume resume = QResume.resume;! Predicate predicate =! resume.firstName.containsIgnoreCase(search).! or(resume.lastName.containsIgnoreCase(search));! return resumeRepository.findAll(predicate, pageable);! }!}!!// Spring generates an Implementation for the ResumeRepository!public interface ResumeRepository extends! PagingAndSortingRepository<Resume, Long>,! QueryDslPredicateExecutor<Resume>,! ResumeRepositoryCustom {!}

Page 35: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH

Hot Code Replacement also known as Hot SwappingAllows exchanging of classes and/or resources in a running application or debugging session.!

!

!

!

!

Spring-Loaded can reload class definitions with changed method signaturesSimilar to JRebel but less powerfullIntegrated in gradle bootRun

35

Hot Code Replacement

Page 36: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 36

Testing with Spring IoC is available in testsProfiles allow custom test injection setupDatabase can be used for integration testsMockServlet available

Page 37: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 37

Profiles and Properties@ContextConfiguration(classes = TestApplication,! loader = SpringApplicationContextLoader)!@ActiveProfiles("test") // activate test profile!@Transactional // rollback after each test method!abstract class TestSpec extends Specification {!}

spring.datasource.driverClassName=org.h2.Driver!spring.datasource.password=!spring.datasource.url=jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000!spring.datasource.username=sa!spring.jpa.database-platform=org.hibernate.dialect.H2Dialect!spring.jpa.hibernate.ddl-auto=create!spring.jpa.show-sql=false

Test Baseclass

application-test.properties

Page 38: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 38

Testing Controllersdef "get resume should get 20 results"() {! when:! ResultActions result = mockMvc.perform(get('/api/resume')! .contentType(MediaType.APPLICATION_JSON)! )!! then:! result.andExpect(status().isOk())! result.andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))! result.andExpect(jsonPath('$.content', hasSize(20)))!}

Page 39: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 39

ConclusionAngularJS

removes server stateenables native application experienceputs view logic into Javascriptrequires new skill profiles and processes!

Spring 4 introduces Convention over Configuration with Spring Bootcomes with ecosystem of sub projectsallows for efficient development

Page 40: Testdrive AngularJS with Spring 4

© 2014 INFINIT GmbH 40

Thank Your For Your Attention

Visit Us Van-der-Smissen-Straße 322767 Hamburg

!

Tel: +49 (0)40 38 65 18 80Fax: +49 (0)40 38 65 18 81

!

Web: www.infinit-group.deE-Mail:[email protected]