How to get Keycloak working with Docker

NOTE: This article might be being revised continuously because of new insights.

This blog describes how I created a couple of Docker images to demonstrate Keycloak.
Important in this blog is that the whole process will be described. I attended a couple of keycloak sessions during Javaone this year and during these sessions the illusion was created that adding Keycloak as the security provider for your application is very easy and almost non-invasive for your code.
What they did not tell you that configuring a server that could use keycloak was not as trivial.
This blog will also expose a java web application with rest end-points to show how the auth works.

This blog will tell all :-)

more >>

Java Maven AngularJS seed project

Journey

In this article I’ll try to find the answer to a couple of basic questions like:

  • Is there a marriage possible between AngularJS, Java and Maven?
  • Can it be done?
  • Should it be done?
  • Must it be done?

Here I’ll describe the whole process of making a Java Maven AngularJS seed project.
This process will include the mistakes I make and the lessons learned.
The whole goal is to create a setup that can be build by a Java developer, with no specific front-end skills.

Goals

  • Maven as the build tool
  • Java EE 7 as the back-end language
  • CDI
  • AngularJS as the frond-end framework
  • Frond-end testing must also be done during the maven build
  • Bower as the web package manager.
  • Use Git as version control
  • Bootstrap als css standard
  • Javascript in src/main/javascript
  • Minifying javascript
  • Jasmine as javascript unit test tool integrated with maven

Prerequisites

The project is hosted here

Basic maven project

1
mvn archetype:generate -DgroupId=nl.ivonet -DartifactId=java-angularjs-seed -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

After generating I got this:

1
2
3
4
5
6
7
8
./pom.xml
./src
./src/main
./src/main/resources
./src/main/webapp
./src/main/webapp/index.jsp
./src/main/webapp/WEB-INF
./src/main/webapp/WEB-INF/web.xml

Now we start configuring it.

First create test space and remove unnecessary files:

Open a terminal in the root of the project. (from now on all commands are assumed to be executed from the root of the project)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mkdir -p src/main/java
mkdir -p src/test/java
mkdir -p src/test/javascript/unit
mkdir -p src/test/javascript/e2e
mkdir -p src/test/resources
rm -f ./src/main/webapp/WEB-INF/web.xml
rm -f ./src/main/webapp/index.jsp
mkdir -p ./src/main/webapp/css
touch ./src/main/webapp/css/specific.css
mkdir -p ./src/main/webapp/js
touch ./src/main/webapp/js/app.js
touch ./src/main/webapp/js/controllers.js
touch ./src/main/webapp/js/routes.js
touch ./src/main/webapp/js/services.js
touch ./src/main/webapp/js/filters.js
touch ./src/main/webapp/js/services.js
mkdir -p ./src/main/webapp/vendor
mkdir -p ./src/main/webapp/partials
mkdir -p ./src/main/webapp/img
touch README.md
touch .bowerrc

Initialize npm

As I want to use npm so I want to initialize it.

1
npm init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
name: (java-angularjs-seed)
version: (0.0.0)
description: A starter project for AngularJS combined with java and maven
entry point: (index.js)
test command: karma start test/resources/karma.conf.js
git repository: https://github.com/ivonet/java-angular-seed
keywords:
author: Ivo Woltring
license: (ISC) Apache 2.0
About to write to /Users/ivonet/dev/ordina/LabTime/java-angularjs-seed/package.json:
{
"name": "java-angularjs-seed",
"version": "0.0.0",
"description": "A starter project for AngularJS combined with java and maven",
"main": "index.js",
"scripts": {
"test": "karma start test/resources/karma.conf.js"
},
"repository": {
"type": "git",
"url": "https://github.com/ivonet/java-angular-seed"
},
"author": "Ivo Woltring",
"license": "Apache 2.0",
"bugs": {
"url": "https://github.com/ivonet/java-angular-seed/issues"
},
"homepage": "https://github.com/ivonet/java-angular-seed"
}
Is this ok? (yes)

adjust the ./package.json file. It needs a bit more tweaking:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"name": "java-angular-seed",
"private": true,
"version": "0.0.0",
"description": "A starter project for AngularJS combined with java and maven",
"repository": "https://github.com/ivonet/java-angular-seed",
"license": "Apache 2.0",
"devDependencies": {
"bower": "^1.3.1",
"http-server": "^0.6.1",
"karma": "~0.12",
"karma-chrome-launcher": "^0.1.4",
"karma-firefox-launcher": "^0.1.3",
"karma-jasmine": "^0.1.5",
"karma-junit-reporter": "^0.2.2",
"protractor": "~0.20.1",
"shelljs": "^0.2.6"
},
"scripts": {
"postinstall": "bower install",
"prestart": "npm install",
"start": "http-server src/main/webapp -a localhost -p 8000",
"pretest": "npm install",
"test": "karma start src/test/javascript/karma.conf.js",
"test-single-run": "karma start src/test/javascript/karma.conf.js --single-run",
"preupdate-webdriver": "npm install",
"update-webdriver": "webdriver-manager update",
"preprotractor": "npm run update-webdriver",
"protractor": "protractor src/test/javascript/protractor-conf.js",
"update-index-async": "node -e \"require('shelljs/global'); sed('-i', /\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/, '//@@NG_LOADER_START@@\\n' + cat('src/main/webapp/vendor/angular-loader/angular-loader.min.js') + '\\n//@@NG_LOADER_END@@', 'src/main/webapp/index.html');\""
}
}

Add Bower

I want to use bower as the the web package manager.

The default place bower will install its dependencies is ./bower-components but as I want them to conform to the maven structure I will add a directive to the .bowerrc file we just created.
Information hiding dictates that I won’t tell users that I manage stuff with bower.

1
2
3
{
"directory": "src/main/webapp/vendor"
}

This will tell bower to copy the downloaded packages to that place.

Add dependencies (by hand)

1
2
3
4
5
6
bower install angular#1.3.0-beta.14
bower install angular-route#1.3.0-beta.14
bower install angular-animate#1.3.0-beta.14
bower install angular-mocks#1.3.0-beta.14
bower install angular-loader#1.3.0-beta.14
bower install bootstrap

This should be enough for a first basic setup. You might want to change the versions.

Because I want to have an easy setup I want to eliminate all these manual steps so now I want to have a config file that helps me to do this automagically :-).
The following command will help create a setup script for bower.

1
bower init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[?] name: java-angularjs-seed
[?] version: 0.0.0
[?] description: A java / maven / angularjs seed project
[?] main file: src/main/webapp/index.html
[?] what types of modules does this package expose?
[?] keywords: java,maven,angularjs,seed
[?] authors: IvoNet <webmaster@ivonet.nl>
[?] license: Apache 2.0
[?] homepage: http://ivonet.nl
[?] set currently installed components as dependencies? Yes
[?] add commonly ignored files to ignore list? Yes
[?] would you like to mark this package as private which prevents it from being accidentally pub[?] would you like to mark this package as private which prevents it from being accidentally published to the registry? Yes
...
[?] Looks good? (Y/n) Y

The resulting ./bower.json file should look something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"name": "java-angularjs-seed",
"version": "0.0.0",
"authors": [
"IvoNet <webmaster@ivonet.nl>"
],
"description": "A java / maven / angularjs seed project",
"keywords": [
"java",
"maven",
"angularjs",
"seed"
],
"license": "Apache 2.0",
"homepage": "http://ivonet.nl",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"src/main/webapp/vendor",
"test",
"tests"
],
"dependencies": {
"angular": "1.3.0-beta.14",
"angular-loader": "1.3.0-beta.14",
"angular-mocks": "1.3.0-beta.14",
"angular-route": "1.3.0-beta.14",
"bootstrap": "3.2.0"
},
"main": "src/main/webapp/index.html"
}

Now that I have this config file I should be able to add the dependencies with a command.
But first to test it We need to remove the manually added dependencies :-)

1
rm -rf ./src/main/webapp/vendor

Now to test the try out the setup:

1
npm install

It should recreate it all again :-)

It will also add a folder node_modules to the project.
These folders should be excluded from version control, but that will be done later

Add Karma

I want to use Karma as the testrunner for javascript.
Create the ./src/test/javascript/karma.conf.js containing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
module.exports = function(config){
config.set({
basePath : '../../../',
files : [
'src/main/webapp/vendor/angular**/**.min.js',
'src/main/webapp/vendor/angular-mocks/angular-mocks.js',
'src/main/webapp/js/**/*.js',
'src/test/javascript/unit/**/*.js'
],
autoWatch : true,
frameworks: ['jasmine'],
browsers : ['Chrome'],
plugins : [
'karma-chrome-launcher',
'karma-firefox-launcher',
'karma-jasmine',
'karma-junit-reporter'
],
junitReporter : {
outputFile: 'target/test_out/unit.xml',
suite: 'src/test/javascript/unit'
}
});
};

Now we can run the unit tests by using the command npm test

Add Protractor

I want to use protractor as the end-2-end tester.
Create the ./src/test/javascript/protractor-conf.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'use strict';
exports.config = {
allScriptsTimeout: 11000,
specs: [
'e2e/*.js'
],
capabilities: {
'browserName': 'chrome'
},
baseUrl: 'http://localhost:8000/',
framework: 'jasmine',
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
}
};

Now we can run the integration tests by using the command npm run protractor

Add html

index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!doctype html>
<html lang="en" data-ng-app="app">
<head>
<meta charset="utf-8">
<title>Seed App</title>
<link rel="stylesheet" href="vendor/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/specific.css">
</head>
<body>
<div ng-view></div>
<script src="vendor/angular/angular.min.js"></script>
<script src="vendor/angular-route/angular-route.min.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
<script src="js/routes.js"></script>
<script src="js/services.js"></script>
</body>
</html>

partials/home.html:

1
<p>{{title}}</p>

Add hello world AngularJs

js/apps.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'use strict';
var app = angular.module('app', [
'ngRoute',
'controllers'
]);
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'partials/home.html',
controller: 'HomeController'
})
.otherwise({
redirectTo: '/'
});
}]);

js/controlers.js:

1
2
3
4
5
6
7
'use strict';
var controllers = angular.module("controllers", []);
controllers.controller("HomeController", ['$scope', function ($scope) {
$scope.title = 'Hello world!';
}]);

Add Jasmine tests

src/test/javascript/unit/controllersSpec.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'use strict';
/* jasmine specs for controllers go here */
describe('Controller tests', function () {
describe('HomeController', function () {
var scope, ctrl;
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('HomeController', {$scope: scope});
}));
it('should contain hello world', function () {
expect(scope.title).toBe('Hello world!');
});
});
});

Add end-2-end tests

src/test/javascript/e2e/scenarios.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'use strict';
/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */
describe('App integration tests', function () {
beforeEach(function () {
browser.get('/index.html');
});
it('should redirect index.html to index.html#/', function () {
browser.getLocationAbsUrl().then(function (url) {
expect(url.split('#')[1]).toBe('/');
});
});
});

Test basic setup

For this we need two terminal windows opened in the root of the project

In one of the terminals start the test server

1
npm start

Go here in a browser. You should see “Hello world!”

In the other terminal try out:

1
npm run progractor

This should result in an executed e2e test with no errors.

Try out:

1
npm test

This should result in a working unit test and it should keep on monitoring for test changes.
If you change the test in controllersSpec.js to a wrong text you should see this almost immediately in the terminal window.

Add Java EE 7

I want to be able to use the EE 7 stack.

Adjust the pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>nl.ivonet</groupId>
<artifactId>java-angularjs-seed</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>java-angularjs-seed Maven Webapp</name>
<url>http://ivonet.nl</url>
<properties>
<artifact.name>app</artifact.name>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${artifact.name}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArguments>
<endorseddirs>${endorsed.dir}</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${endorsed.dir}</outputDirectory>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>javax</groupId>
<artifactId>javaee-endorsed-api</artifactId>
<version>7.0</version>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

CDI

Now we want to have CDI so we need ths file ./src/main/webapp/WEB-INF/beans.xml:

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="annotated">
</beans>

We now have a basic maven project that can be build by the standard maven commands.

Intermezzo

So now I have a working project that can contain java and angular with all its functionality. The trouble is there is no integration yet, no version control and no maven build that does it all.

I don’t even know if it can be done with currently available tools.

Goals met:

  • AngularJS as the frond-end framework
  • Bower as the web package manager.

Goals partially met:

  • Maven as the build tool, but no javascript tests yet
  • Java EE 7 as the back-end language, but no example yet
  • CDI, but no example yet
  • bootstrap
  • Javascript unit testing

Goals to be met:

  • Maven as the build tool
  • Java EE 7 as the back-end language - with example
  • Frond-end testing must also be able done during the maven build
  • Use Git as version control
  • Maven integration for javascript testing
  • Maven for minifying javascript
  • Javascript jslint
  • Javascript as resource

Add Version Controll

I use Git for this.

add a .gitignore file to the root of the project containing:

1
2
3
.idea/
.DS_Store
node_modules/

add a .gitignore file to the src/main/webapp of the project containing:

1
vendor/

now create the git repository:

1
2
3
git init
git add .
git commit -m "initial import"

Add Java example

In order to get make it possible to create restful services we need a root context for these services. As I want to talk json I also define a json provider.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package nl.ivonet.application;
import nl.ivonet.controler.HomeControler;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.Set;
/**
* Basic JAX-RS application.
*
* @author Ivo Woltring
*/
@ApplicationPath("service")
public class SeedApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> resources = new java.util.HashSet<>();
// following code can be used to customize Jersey 2.0 JSON provider:
try {
final Class jsonProvider = Class.forName("org.glassfish.jersey.jackson.JacksonFeature");
// Class jsonProvider = Class.forName("org.glassfish.jersey.moxy.json.MoxyJsonFeature");
// Class jsonProvider = Class.forName("org.glassfish.jersey.jettison.JettisonFeature");
resources.add(jsonProvider);
} catch (final ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(getClass().getName())
.log(java.util.logging.Level.SEVERE, null, ex);
}
addRestResourceClasses(resources);
return resources;
}
/**
* Add your own resources here.
*/
private void addRestResourceClasses(final Set<Class<?>> resources) {
resources.add(HomeControler.class);
}
}

Now for a Hello world service…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package nl.ivonet.controler;
import nl.ivonet.model.Hello;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* Hello world man.
* @author Ivo Woltring
*/
@Path("/home")
@RequestScoped
public class HomeControler {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Hello get() {
return new Hello("world");
}
}

Change the HomeController.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'use strict';
var controllers = angular.module("controllers", []);
controllers.controller("HomeController", ['$scope', '$http', function ($scope, $http) {
$scope.debug = true;
$scope.title = 'Hello ';
$http.get("service/home").success(function (data) {
$scope.data = data;
$scope.title += $scope.data.message;
});
$scope.toggleDebug = function () {
$scope.debug = !$scope.debug;
};
}]);

And the home.html

1
2
3
4
<p>{{title}}</p>
<div>
<pre data-ng-model="debug" data-ng-show="debug">{{ data | json }}</pre>
</div>

For this all to work we need a Java EE Container and Node.js just won’t do :-)
I work with Glassfish for my test environment and deployed it.

And now we get:

Refactoring…

I stared a bit at the code and concluded that I’m not happy with the current state of affairs. Right now I have my javascript code in the src/main/webapp/js folder and it shouldn’t be there. According to maven rulez it should be in src/main/javascript just like java. I also want to have a minified javascript in the artifact and not the origional.

So added the folder and told maven it needed to recognize it as a resource folder.

Add the folling config snippet to the <build> tag in the pom.xml

1
2
3
4
5
6
<resources>
<resource>
<directory>src/main/javascript</directory>
<filtering>true</filtering>
</resource>
</resources>

now I moved all files in the js folder of the webapp to that folder and removed the js folder in webapp.
Now I wanted the minifacation to work and the jslint. I used a maven plugin for that. After some trials and errors the following config seems to work the best.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>compress-js</id>
<goals>
<goal>jslint</goal>
<goal>compress</goal>
</goals>
</execution>
</executions>
<configuration>
<failOnWarning>true</failOnWarning>
<outputDirectory>target/app/js/</outputDirectory>
<nosuffix>true</nosuffix>
<excludes>
<exclude>vendor/**</exclude>
<exclude>**/*min.css</exclude>
<exclude>**/*min.js</exclude>
</excludes>
</configuration>
</plugin>

and a small change to the karma.conf.js. Change src/main/webapp/js/**/*.js to src/main/javascript/**/*.js. now the npm test should work again.

The mvn clean package command failed because my app.js didn’t get through the jslint step and that is just what I wanted. Fixed it and voila.

The code until now is checked in.

Intermezzo

Goals met:

  • AngularJS as the frond-end framework
  • Bower as the web package manager.
  • Javascript jslint
  • Javascript as resource
  • Javascript testing with Karma / Jasmine
  • Maven for minifying javascript
  • Maven as the build tool
  • Java EE 7 as the back-end language
  • bootstrap
  • CDI
  • Use Git as version control

Goals to be met:

  • Frond-end testing must also be able done during the maven build
  • Maven integration for javascript testing

So I guess that my main goal now is to get jasmine working with maven…

Add jasmine maven plugin

At first I removed all jasmine pom stuff to start from scratch. Then I added the most basic jasmine config to it.
But that didn’t work at all. Which was actually logical but I had to do some reading to understand that :-). I had to tell (in the correct order) what the plugin already needs to know before it can test my personal javascript code. At this time this means that I have to tell it to load JQuery and Angular and the Angular mocks. That seemed to do a bit more but now it didn’t find any *Spec.js files and I had to tell the plugin where to find that and the sources.
This seemed to work but I got terrible stacktraces. After some research I found out that it had nothing to do with my configuration but with htmlunit, which is used by default by the plugin. So I had to install phantomjs and configure it as the driver.

1
brew install phantomjs

Or install it according to the these instructions.

The plugin configuration below finally worked.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<plugin>
<groupId>com.github.searls</groupId>
<artifactId>jasmine-maven-plugin</artifactId>
<version>1.3.1.5</version>
<executions>
<execution>
<goals>
<goal>test</goal>
<goal>bdd</goal>
</goals>
</execution>
</executions>
<configuration>
<webDriverClassName>org.openqa.selenium.phantomjs.PhantomJSDriver</webDriverClassName>
<preloadSources>
<source>${vendor.loc}/jquery/dist/jquery.js</source>
<source>${vendor.loc}/angular/angular.min.js</source>
<source>${vendor.loc}/angular-route/angular-route.min.js</source>
<source>${vendor.loc}/angular-mocks/angular-mocks.js</source>
</preloadSources>
<jsSrcDir>src/main/javascript</jsSrcDir>
<jsTestSrcDir>src/test/javascript/unit</jsTestSrcDir>
<specIncludes>
<include>*Spec.js</include>
</specIncludes>
</configuration>
</plugin>

Add bower to maven

As I want to have the integration as complete as possible I want to have the initial bower install also part of maven. Again some research and for me the maven-exec-plugin seems a good solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>bower</executable>
<arguments>
<argument>install</argument>
</arguments>
<workingDirectory>${basedir}</workingDirectory>
</configuration>
</plugin>

Now during the generate-source phase it will run bower and put all the javascript dependencies into src/main/webapp/vendor because that has been configured in .bowerrc and bower.json. Good stuff :-)

Clean

As the vendor folder in webapp is not part of our version control and cleanup is also part of the fun I added the maven clean plugin with a bit of configuration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<configuration>
<filesets>
<fileset>
<directory>src/main/webapp/vendor</directory>
</fileset>
<fileset>
<directory>node_modules</directory>
</fileset>
</filesets>
</configuration>
</plugin>

Removed protractor

As I tend to use Fitnesse and Selenium (in comination) to do my end-to-end testing I decided to remove protractor from the project. I want to test against the real server with all stuff working before running e2e tests.

I had to refactor some stuff like the path to the javascript unit tests as I moved the other scripts to src/test/javascript and removing references to protractor.

Conclusion (for now :-))

I committed the whole seed project to github and except for the end to end tests it all seems to work. I don’t mind about that because I tend to use Fitnesse for these kinds of tests so I can test against the real server.

It is a learning process and to say that I’m completely happy with the configuration is to lie.

In this version of the seed project it was all about integration. Integrating the different worlds of front-end and back-end with all it’s tools.

The question remains if that should be the way to go? I don’t know yet. Decoupling these worlds might be better.

I tend to think that these worlds should only meet through services and the front-end and back-end should be build separately. But as I was writing this a colleague of mine told me that he really wanted a setup like this because he wanted the java developers to be able to build the whole project with a simple command like mvn clean install, because it cuts down on the learning curve.

Final conclusion: Who am I to judge?!

Extra’s

IDE Configuration

I assume IntelliJ because lets be real… There can be only one :-)
I also assume that glassfish is already installed.

The first time you check out the project you should run mvn package or mvn exec:exec to get the vendor resources installed.

Now you can deploy to glassfish through the IDE but you will still not have the javascript resources copied to the right place. In order to get this to work you can tell IntelliJ to perform a step before every make (see the picture below)

Choose on yuicompressor:compress the right mouse option: Execute Before Make.

Now whenever you press cmd+F9 or ctrl+r the compressor plugin will do its thing and update the resources.

Cheerz,

Ivo.

Open Questions

  • Is Yeoman something to look at?
  • What about grunt? is it better?
  • Or Gulp?
  • Should I make the back-end and frond-end separate projects so they can be developed separately?
  • JHipster?
  • run with jetty
  • jslint as part of the config again
  • webjars

Related topics

Java Maven Angular Seed Project

In this article I’ll try to find the answer to a couple of basic questions like:

  • Is there a marriage possible between AngularJS, Java and Maven?
  • Can it be done?
  • Should it be done?
  • Must it be done?

Here I’ll describe the whole process of making a Java Maven AngularJS seed project.
This process will include the mistakes I make and the lessons learned.
The whole goal is to create a setup that can be build by a Java developer, with no specific front-end skills.

more >>