Building Angular WebBundle for Apache Karaf

Apache Karaf is a complete applications container, supporting several programming models: OSGi, DS, Blueprint, Spring, …

It’s also a complete web application container like Apache Tomcat, but providing some unique feature. If Apache Karaf supports WAR, it also supports Web Bundle. A Web Bundle is basically a bundle with a specific header.

For web application frontend, Angular is popular framework (used in combination with Bootstrap). It’s possible to write Angular directly by hand, but, most of the time, web developers prefer to use IDE like WebStorm.
In any case, Angular CLI (https://github.com/angular/angular-cli) is a “classic” tool to test and build your Angular application.

Project with Angular CLI

Angular CLI allows you to quickly start your Angular project.

You can bootstrap using the following command:

$ ng new test-frontend

Then, Angular CLI (ng) creates all required resources:

  create test-frontend/README.md (1028 bytes)
  create test-frontend/.angular-cli.json (1248 bytes)
  create test-frontend/.editorconfig (245 bytes)
  create test-frontend/.gitignore (544 bytes)
  create test-frontend/src/assets/.gitkeep (0 bytes)
  create test-frontend/src/environments/environment.prod.ts (51 bytes)
  create test-frontend/src/environments/environment.ts (387 bytes)
  create test-frontend/src/favicon.ico (5430 bytes)
  create test-frontend/src/index.html (299 bytes)
  create test-frontend/src/main.ts (370 bytes)
  create test-frontend/src/polyfills.ts (3114 bytes)
  create test-frontend/src/styles.css (80 bytes)
  create test-frontend/src/test.ts (642 bytes)
  create test-frontend/src/tsconfig.app.json (211 bytes)
  create test-frontend/src/tsconfig.spec.json (283 bytes)
  create test-frontend/src/typings.d.ts (104 bytes)
  create test-frontend/e2e/app.e2e-spec.ts (295 bytes)
  create test-frontend/e2e/app.po.ts (208 bytes)
  create test-frontend/e2e/tsconfig.e2e.json (235 bytes)
  create test-frontend/karma.conf.js (923 bytes)
  create test-frontend/package.json (1298 bytes)
  create test-frontend/protractor.conf.js (722 bytes)
  create test-frontend/tsconfig.json (363 bytes)
  create test-frontend/tslint.json (3012 bytes)
  create test-frontend/src/app/app.module.ts (316 bytes)
  create test-frontend/src/app/app.component.css (0 bytes)
  create test-frontend/src/app/app.component.html (1141 bytes)
  create test-frontend/src/app/app.component.spec.ts (986 bytes)
  create test-frontend/src/app/app.component.ts (207 bytes)

> uws@9.14.0 install /home/jbonofre/Workspace/test-frontend/node_modules/uws
> node-gyp rebuild > build_log.txt 2>&1 || exit 0


> node-sass@4.7.2 install /home/jbonofre/Workspace/test-frontend/node_modules/node-sass
> node scripts/install.js

Cached binary found at /home/jbonofre/.npm/node-sass/4.7.2/linux-x64-59_binding.node

> uglifyjs-webpack-plugin@0.4.6 postinstall /home/jbonofre/Workspace/test-frontend/node_modules/webpack/node_modules/uglifyjs-webpack-plugin
> node lib/post_install.js


> node-sass@4.7.2 postinstall /home/jbonofre/Workspace/test-frontend/node_modules/node-sass
> node scripts/build.js

Binary found at /home/jbonofre/Workspace/test-frontend/node_modules/node-sass/vendor/linux-x64-59/binding.node
Testing binary
Binary is fine

added 1272 packages in 65.41s
Project 'test-frontend' successfully created.

You can test your project using:

$ ng serve
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Date: 2018-03-02T10:01:07.769Z                                                          
Hash: a130567e5011eb653504
Time: 8240ms
chunk {inline} inline.bundle.js (inline) 3.85 kB [entry] [rendered]
chunk {main} main.bundle.js (main) 17.9 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 549 kB [initial] [rendered]
chunk {styles} styles.bundle.js (styles) 41.5 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js (vendor) 7.42 MB [initial] [rendered]

webpack: Compiled successfully.

You can access your web application directly in your browser and all change is taken on the fly.

If you want to generate “static” web application (converting your Angular project as a set of html and js files), you can use:

$ ng build
Date: 2018-03-02T10:03:36.703Z                                                          
Hash: 9de4cb2f98111fe59f9e
Time: 9459ms
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 3.89 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 7.39 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 202 kB [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 14.5 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.44 MB [initial] [rendered]

ng build generates all static content in a dist folder. You will find here a index.html file with all other resources (js files, …).

Building Angular application ready to be deployed in Apache Karaf

Now, we want to “automatize” this build using Apache Maven. The purpose is to:

  1. perform ng build to create the resources in the dist folder
  2. package this resource as a Web Bundle

Our Maven pom.xml will contain basically three plugin executions:

  1. maven-antrun-plugin to perform the ng build
  2. maven-bundle-plugin to package as a WebBundle
  3. maven-clean-plugin to clean all created folder

Here’s the pom.xml doing so:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>net.nanthrax</groupId>
    <artifactId>test-frontend</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>bundle</packaging>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.8</version>
                <executions>
                    <execution>
                        <id>ng-build</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <mkdir dir="target"/>
                                <echo message="Generating frontend resource"/>
                                <exec executable="ng">
                                    <arg value="build"/>
                                </exec>
                            </target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>3.3.0</version>
                <inherited>true</inherited>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Web-ContextPath>/angular-test</Web-ContextPath>
                        <Private-Package>*</Private-Package>
                        <Include-Resource>dist</Include-Resource>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-clean-plugin</artifactId>
                    <configuration>
                        <directory>dist</directory>
                        <directory>target</directory>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

</project>

The most important parts are:

  • Web-ContextPath is the context path of our web application.
  • Include-Resource uses the resources content generated by ng build.

We can now build our bundle, simply using:

$ mvn clean install

Deployment in Apache Karaf

Now, the deployment is pretty simple in Apache Karaf.

On a running Karaf instance, you just have to install the war feature:

karaf@root()> feature:install war

Now, we directly install our WebBundle:

karaf@root()> bundle:install -s mvn:net.nanthrax/test-frontend/1.0-SNAPSHOT
Bundle ID: 87

We can see the web application available:

karaf@root()> web:list
ID │ State       │ Web-State   │ Level │ Web-ContextPath │ Name
───┼─────────────┼─────────────┼───────┼─────────────────┼───────────────────────────────
87 │ Active      │ Deployed    │ 80    │ /angular-test   │ test-frontend (1.0.0.SNAPSHOT)

And we can access the application directly using a browser on the Karaf HTTP service (listening on 8181 by default): http://localhost:8181/angular-test

Conclusion

Thanks to the Maven build, we can now setup continuous integration (using Jenkins for instance).

A good improvement could be to wrap this Angular web application in a Karaf feature, automatically installing the war feature and our webbundle.

You May Also Like

About the Author: jbonofre

ASF Member, PMC for Apache Karaf, PMC for Apache ServiceMix, PMC for Apache Archiva, PMC for Apache Felix, PMC for Apache Camel, PMC for Apache Syncope, PMC for Apache Beam, PMC for Apache CarbonData, PMC for Apache Bahir, PMC for Apache Brooklyn, PMC for Apache Falcon, PMC for Apache Guacamole, PMC for Apache Lens, Committer for Apache ActiveMQ and much more ! Twitter: jbonofre IRC: jbonofre on #servicemix,#karaf,#camel,#cxf on Freenode