Introduction

Amdatu Blueprint provides a rich, stable/tested and useable application blueprint based on open-source components.

Blueprint workspace

The Blueprint Workspace is a standard Bndtools workspace template that is setup and configured to allow application developers to hit the ground running. It provides (minimal tooling and a complete application skeleton with all common frameworks, components and pattern examples in place.

The Blueprint Workspace is managed and versioned and as such encapsulates the version control of the application skeleton. Hence applications are implemented based on an Application Blueprint version and do not individually manage underlying versions. For that reason the workspace is setup in such a way that allows it to be updated easily.

Goals:

  • Getting started in 15 minutes

  • Easy to update Blueprint version

  • Promotes Blueprint best practices

Getting started

Prerequisites

Install Eclipse

Amdatu Blueprint comes with an Eclipse profile to be used with the Eclipse installer. With this profile you can create an Eclipse installation with the required plugins and configuration to get working with Amdatu Blueprint.

  • Download the Eclipse installer if you don’t already have that installed (Eclipse downloads)

  • Launch the Eclipse installer

  • Select advanced mode

  • Add the Amdatu Blueprint product (AmdatuBlueprint.setup)

  • Install the Amdatu Blueprint product.

15 Minutes tutorial

In this tutorial we will implement a todo application

Create workspace

Each Bnd(tools) workspace requires a Configuration project which is usually named "cnf". This project contains workspace wide configuration. The project looks like a normal Java project in Eclipse, but contains a build.bnd file, this file contains the workspace configuration.

To create this cnf project:

File → New → Bnd OSGi Workspace → Next → Select "Amdatu Blueprint" → Next → Finish

Todo service api

Now we have a workpace we can get started on our todo application. We’ll start with the most important part and define a todo service api.

For this we create a new project using the Api project template.

File → New → Bnd OSGi Project → Select "Api project" → Next → Project Name: "org.amdatu.tutorial.todo.api" → Next → Finish

This will create a new api project named org.amdatu.tutorial.todo.api in our workspace.

Because this code is our API, we want other bundles to be able to see the classes. For this we have to "export" the package. Only exported packages can be imported by other bundles. Packages that are not exported are only visible to the bundle itself.

To export the package open the "Contents" tab of the bnd.bnd file and drag the package (org.amdatu.tutorial.todo.api) to the "Export Packages" section. Note that Bndtools proposes a version for the package. Exported packages are versioned, which is important to specify API compatibility.

Now create the Todo class and TodoService interface as:

Todo.java
package org.amdatu.tutorial.todo.api;
public class Todo {

    private String id;
    private String description;
    private boolean completed;
    private String user;

    public Todo() {

    }

    public Todo(String description, String user) {
        this.id = null;
        this.description = description;
        this.user = user;
        this.completed = false;
    }


    public Todo(String id, String description, String user, boolean completed) {
        this.id = id;
        this.description = description;
        this.user = user;
        this.completed = completed;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean isCompleted() {
        return completed;
    }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

}
TodoService.java
package org.amdatu.tutorial.todo.api;
import java.util.List;

public interface TodoService {

    List<Todo> list(String user);

    void store(Todo todo);

}

Todo service implementation

With the contract in place we can create an implementation of our TodoService. For this we have to create another project, this time we use the Implementation project template.

File → New → Bnd OSGi Project → Select "Implementation project" → Next → Project Name: "org.amdatu.tutorial.todo.memory" → Next → Finish

The new project is in the same workspace as the api we created in the previous step but we can’t use that api yet. We first have to add the api to the new project’s build path. Open the Build tab in the bnd.bnd file and add the org.amdatu.tutorial.todo.api bundle to the build path and save the file.

Now implement the InMemTodoService:

InMemTodoService.java
package org.amdatu.tutorial.todo.memory;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

import org.amdatu.tutorial.todo.api.Todo;
import org.amdatu.tutorial.todo.api.TodoService;
import org.apache.felix.dm.annotation.api.Component;

@Component
public class InMemTodoService implements TodoService {

    private final List<Todo> todos = new CopyOnWriteArrayList<>();

    @Override
    public void store(Todo todo) {
        todos.add(todo);
    }

    @Override
    public List<Todo> list(String user) {
        return todos.stream()
            .filter(t -> t.getUser().equals(user))
            .collect(Collectors.toList());
    }

}

Besides the @Component annotation there is nothing OSGi specific about this code, it’s just plain Java. The @Component annotation will make the Apache Felix Dependency Manager register our InMemTodoService as a service in the service registry. It will use the interfaces the component implements for this so in this case our InMemTodoService will be registered in the service registry as a TodoService.

Last thing we need to do is include the InMemTodoService in the org.amdatu.tutorial.todo.memory bundle. We do this by opening the "Contents" tab of the bnd.bnd file, and we don’t want others to depend on the implementation details of this service so drag the org.amdatu.tutorial.todo.memory package to the "Private Packages" section.

Todo app

We now have a TodoService contract and an implementation but that’s not really usable yet. Let’s create a web application can be used to manage the todo’s.

We create a new project again using the App project template.

File → New → Bnd OSGi Project → Select "App project" → Next → Project Name: "org.amdatu.tutorial.todo.app"

This template requires us to give give some input:

AppName

The name of he application, use "Todo"

httpContextPath

Http context path for the application http context, use "/todo"

Next → Next → Finish

The project we’ve just created already has some classes already.

  • TodoApp This is a JAX-RS Application with property annotations to make Amdatu Web aware of the application AND Some annotations to register static resources with the OSGi Http Whiteboard.

  • TodoServletContextHelper Servlet context helper, tis makes that the OSGi Http Whiteboard will create a new servlet context for our app.

  • TodoResource A JAX-RS resource for our app

When using the "App" template the instructions to include classes and web resources in the bundle are added to the bundle descriptor automatically.

RESTful webservice

Implement the TodoResource as:

This is a new project again so we’ll need to add the api bundle to the build path of the org.amdatu.tutorial.todo.app project.

TodoResource
package org.amdatu.tutorial.todo.app;

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.amdatu.tutorial.todo.api.Todo;
import org.amdatu.tutorial.todo.api.TodoService;
import org.apache.felix.dm.annotation.api.Component;
import org.apache.felix.dm.annotation.api.Property;
import org.apache.felix.dm.annotation.api.ServiceDependency;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;

@Component(provides = Object.class)
@Property(name = JaxrsWhiteboardConstants.JAX_RS_APPLICATION_SELECT, value = TodoApp.JAX_RS_APPLICATION_NAME)
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class TodoResource {

    @ServiceDependency
    private volatile TodoService todoService;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("{user}")
    public List<Todo> list(@PathParam("user") String user) {
      return todoService.list(user);
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public void saveTodo(Todo todo) {
      todoService.store(todo);
    }

}

The TodoResource is a JAX-RS resource, again we use an @Component annotation to register this in the service registry. As we don’t implement any interface here we need to explicitly state that is should be registered as "Object.class" using the "provides" attribute. Otherwise the component would be instantiated by the dependency manager BUT not registered in the service registry.

No classpath scanning?

If you know JAX-RS from the Java EE world, you might wonder why this service registration is required. The reason is that classpath scanning is considered a bad practice in OSGi. Java doesn’t have any reliable way to do this when multiple class loaders are in play. The good news is we really don’t need classpath scanning in OSGi, since we can rely on the service registry!

Static web resources

When we created the project a web folder was created in the project. This folder will be included in the bundle and the contents of this folder will be available on "/todo/* "

All we need to do is create a web application and put it in the web folder.

index.html
<html ng-app="todoApp">
<head>
  <title>Amdatu tutorial</title>
  <style>
    body {
      background: #dfdfdf;
    }

    div {
      padding: 20px;
      width: 300px;
      margin: 0 auto;
      background: white;
      border-radius: 5px;
    }
  </style>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.12/angular.js"></script>
  <script src="todo.js"></script>
</head>

<body>
  <div ng-controller="TodoController">
    <h2>Todo list</h2>
    <form ng-submit="addTodo()">
      <input type="text" ng-model="todoText" size="30" placeholder="Add new todo here">
      <input type="submit" value="add">
    </form>
    <ul class="unstyled">
      <li ng-repeat="todo in todos track by $index">
        <span>{{todo.description}}</span>
      </li>
    </ul>
  </div>
</body>
</html>
todo.js
angular.module('todoApp', [])
    .controller('TodoController', ['$scope', '$http', function ($scope, $http) {

        $http.get('rest/amdatu').success(function (response) {
            $scope.todos = response;
        });

        $scope.addTodo = function () {
            var todo = {
                "description": $scope.todoText,
                "completed": false,
                "user": "amdatu"
            };

            $http.post('rest', todo).success(function () {
                $scope.todoText = '';
                $scope.todos.push(todo);
            });

        };

        $scope.updateTodo = function (todo) {
            $http.post('rest', todo).success(function () {
                $scope.todoText = '';
                $scope.todos.push(todo);
            });
        }
    }]);

Run

We’re ready for launch! Let’s put the pieces we have just built together, we will do this in a new project that we create based on the Run project template.

File → New → Bnd OSGi Project → Select "Run project" → Next → Project Name: "run"

The "run" project contains a "Run Descriptor" which specifies features and bundles that should be installed.

  • Open the Run tab in the run.bndun file

  • Collapse the "Run Requirements" section and expand "Run Bundles".

  • Click the '+' icon in the 'Run Bundles' section header

    • Add the bundles we have created in the earlier steps (org.amdatu.tutorial.todo.api, org.amdatu.tutorial.todo.app, org.amdatu.tutorial.todo.memory)

    • Finish

  • Open the Source tab in the run.bndun file

  • Add the web feature to the -runfeatures instruction

Now Click "Run OSGi" and open http://localhost:8080/todo/index.html in a browser

Testing the todo service

TODO: write this part

Features

Amdatu Blueprint provides features for common things,

Base

The base feature provides basic infrastructural services used in almost all projects like logging, dependency management and testing libraries.

Because libraries provided by the base feature are commonly used the feature is enabled by default.

Blobstores

The blobstores feature adds support for storing files in the cloud using Amdatu Blobstores.

Email

With the email feature you get support for sending email, this is backed by Amdatu Email. Based on runtime configuration the mail can be sent using SMTP, Amazon Simple Email Service or for development to a mock service that just prints the email to the console.

Configuration

email-transport

Run feature option to select email transport, defaults to mock

mock

Use mock email transport, this will print te email to the console

aws

Use aws email transport, this will send mail using Amazon SES

smtp

Use smtp email transport, this will send mail using SMTP

For more information: Amdatu Email project documentation

Mongodb

With the mongodb feature you get support for Mongodb, this is backed by Amdatu Mongodb.

For more information: Amdatu Mongodb project documentation

Scheduling

With the scheduling feature you get support for running scheduled tasks, this is backed by Amdatu Scheduling.

Shell

With the scheduling feature you get the gogo shell.

For more information: Apache Felix gogo documentation

Security

With the security feature you get support for securing building secure web applications, this is backed by Amdatu Security.

Testing

With the testing feature you get support for integration test projects, this is backed by Amdatu Testing. When used in combination with the mongodb feature additional support for testing with Mongodb is automatically added. When used in combination with the web feature additional support for testing web applications / services is automatically added.

For more information: Amdatu Testing project documentation

Validator

With the validator feature you get support for bean validation, this is backed by Amdatu Validator.

Web

With the web feature you get support for writing web applications and services, this is backed by Amdatu Testing.

For more information: Amdatu Web project documentation

Workspace

Using features

To enable additional features for a project use the -buildfeatures instruction this will add the api’s included in the feature in the project’s build path. The -runfeatures instruction is used to add all the bundles required at runtime to a project.

For most projects you only need to add buid features, run features are only needed for run configurations and integration test projects.

Mongodb example

A few examples how the bnd descriptors what the different kinds of project’s bnd descriptors look like using blueprint features.

Api

An api project often doesn’t need any additional features.

example.api/bnd.bnd
Bundle-Version: 1.0.0
Export-Package: example.api
Implementation

An implementation project using mongodb, only build features are used.

example.impl/bnd.bnd
Bundle-Version: 1.0.0
Private-Package: example.impl

-buildfeatures: mongodb
-buildpath: \
    example.api;version=latest
Integration test

An integration test for the mongodb implementation, the only project type using build and run features

example.itest/bnd.bnd
Test-Cases: ${classes;CONCRETE;ANNOTATED;org.junit.runner.RunWith}
Private-Package: example.itest

-buildfeatures: mongodb, testing
-buildpath: \
    example.api;version=latest

-runfw: org.apache.felix.framework
-runfeatures: mongodb, web
-runbundles: \
    example.api;version=latest
    example.impl;version=latest
Run

A run descriptor to run the project (no build features)

run/example.bndrun
-runfw: org.apache.felix.framework
-runfeatures: mongodb
-runbundles: \
    example.api;version=latest
    example.impl;version=latest

Default features

The Amdatu Blueprint base feature is enabled by default, based on your preferences it’s possible enable more features by default. To make a feature default in a workspace add the update the cnf/build.bnd file and add the a comma separated list of features as if it were a project’s bnd.bnd file.

cnf/build.bnd
# Default features (turned on for all projects)
-buildfeatures.default: mongodb, web
-runbundles.default: mongodb, web

This won’t disable the features enabled by default in the blueprint, so only additional features need to be added.

Adding third party dependencies

The features provided by Amdatu Blueprint will get you started but at some point you’ll most likely need additional dependencies.

Adding bundles

To add additional bundles to your workspace add the maven GAV to the project-deps.maven file in the workspace configuration project (cnf).

For example if you want to query Mongodb using Jongo add the following to the project-deps.maven file

cnf/project-deps.maven
org.jongo:jongo:1.3.0

The project-deps.maven file is the input for a bnd maven repository, for more information on this refer to the bnd documentation

Using bundles

Once added the bundles can be used in the -buildpath and -runbundles instructions of your projects and bnd descriptors.

For the example implementation used in Mongodb example the bnd descriptor using would become

example.impl/bnd.bnd
Bundle-Version: 1.0.0
Private-Package: example.impl

-buildfeatures: mongodb
-buildpath: \
    example.api;version=latest,\
    org.jongo

Extending features

It’s also possible to add an additional bundle to a feature to have it always available when a certain feature is used. For this the we can add some instructions to the build.bnd file in the workspace configuration project (cnf).

cnf/build.bnd
-buildpath.mongodb: \
 	${if;(buildfeaturesMerged[]=mongodb); \
		org.jongo\
	}

-runbundles.mongodb: \
	${if;(runfeaturesMerged[]=mongodb); \
		org.jongo\
	}

From now on Jongo can be used when the mongodb feature is enabled, without adding it to the project build path and / or runbundles

Resources