Freitag, 19. Juni 2015

The whiteboard pattern in a Java 8 world

The Whiteboard Pattern is a design pattern used in OSGi applications. It has been published 2004 as an alternative to the Observer Pattern (see the whitepaper of the OSGi alliance.)

Since 2004 - the era of Java 1.4 - Java has received many improvements. So the implementation of the Whiteboard Pattern is much simpler nowadays.

The use case

Assume we have a class NumberProducer that calculates numbers (e.g. prime numbers or something similar).

NumberProducer is used in different environments with different interfaces to present the numbers to the user. It is also possible that there are multiple components available that can print numbers.

So an interface NumberPrinter is defined which specifies the interface the NumberProducer uses to send the calculated numbers to the printing class:

public interface NumberPrinter {
 String printNumber(long value);
 String getLabel();
}

Finding a implementation of NumberPrinter

But how does the NumberProducer find an implementation of NumberPrinter when it has numbers to be printed? This is exactly what the whiteboard pattern descibes.

In this example we have 3 actors:

  • The NumberProducer
  • Two different implementations of NumberPrinter
To use the whiteboard pattern we need an additional component: the whiteboard (which is implemented by the OSGi registry).
When a bundle that provides an implementation of NumberPrinter is activated, it registers this implementation at the OSGi registry. (This is comparable to sticking a note on the whiteboard.)
When the NumberProducer has numbers to print, it searches the OSGi registry for NumberPrinters ...
.. and calls the printNumber() method on each of them:

Implementation part 1: Implement and register a NumberPrinter

When using OSGi declarative services annotations it's quite simple to register a NumberPrinter at the OSGi registry:

Just implement the interface and add the @Component annotation to your class:

@Component
public class HexPrinter implements NumberPrinter {
 @Override
 public String printNumber(long value) {
  return Long.toHexString(value);
 }

 @Override
 public String getLabel() {
  return "Hex";
 }
}
Hint: DS annotations are meant to be processed by the IDE or building toolchain. Use e.g. Declarative Services Annotations Support Eclipse Plug-In or BndTools for this.

Implementation part 2: Let the NumberProducer find and use NumberPrinters

The OSGi Framework provides the BundleTracker that helps finding services that implement specific interfaces.

I decorated this class with a method that takes a closure and applies it to all matching services (see complete code at GitHub):

...
 public void forAllServices(Consumer<S> func) {
  S[] services = getServicesAsArray();
  for (S service : services) {
   func.accept(service);
  }
 }
...

Using this helper class makes the implementation of the NumberProducer simple (complete code):


public class NumberProducer {

 private void printNumberOnPrinters(long number,
   WhiteboardAccess<NumberPrinter> whiteboardAccess) {

  whiteboardAccess.forAllServices((srv) -> {
   System.out.println(srv.printNumber(number));
  });
 }
... 

That's it

As we saw, the implementation of the whiteboard pattern is fast, simple and nearly fail-safe when we're using modern technologies.

Links

Mittwoch, 18. Februar 2015

Docker by example

When I started learning Docker I didn't find a short step by step guide so I decided to make my own. Although I describe a specific use case, I think these steps can easily be transferred to other use cases.

The use case

Prepare a project specific development environment that can be fast and easily installed on developer machines. I'm using a docker image for this since it can not only provide the IDE but also the complete tool development chain such as JDKs and build tools like ant or gradle.

Terms

First of all its important to understand the difference between containers and images.
image
Think of an image as a template for creating containers. It contains a filesystem and some definitions (e.g. maintainer and the ports to export etc.). An image can be based on another image and can itself be the basis for other images.
container
A container is an instance of an image. Running an image creates a new container based on that image.

Dockerhub

Search Dockerhub for an image that can be used as a base for the container.

Pull the image from dockerhub

sudo docker pull joemat/docker-eclipse-for-rcp

This step is optional: When calling docker run with an image that isn't available locally the image is pulled automatically.

Create and run a container based on this image

xhost +local; sudo docker run --name eclipse_luna -it -v /home/joemat/development:/share/development -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix joemat/docker-eclipse-for-rcp

  • The --name parameter assigns a name to the container that can be used to identify it in subsequent commands.
  • The xhost +local command and the -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix parameters are needed to enable the container to create X windows (see Fábio Rehms Blog).
  • -v /home/joemat/development:/share/development "mounts" the local directory /home/joemat/development as /share/development in the container.
  • The container is shut down when the application (eclipse) terminates.
Now it's time to adjust the application to our needs - e.g. create and configure Eclipse workspaces, install additional software etc.
These docker commands may be useful at this point:

  • sudo docker start -i eclipse_luna

    Restarts the docker container when it is stopped (e.g. after eclipse terminated)
    • No need to pass the -v and -e parameters again.
  • sudo docker exec -ti eclipse_luna /bin/bash

    Executes a different application (a shell in this case) in the running container. This could e.g. be used install additional software packages.

Create an image from the current container status

When all configuration and adjustment has been done, it's time to save the current state of the container. This is done using the docker commit command, which creates an image from a container. (The container should be stopped before running docker commit.)

sudo docker commit -m "eclipse for project foo" eclipse_luna eclipse_for_foo

Share the image

Now it's possible the push the image to the docker hub using the docker push command. Alternatively you can push it to your local docker repository. (The Howto from Nik van der Ploeg describes the installation of a private registry server.) Pushing to the local docker registry needs these steps:
  • sudo docker login http://mydockerregistry.foo.com:8080

    Login to the docker registry mydockerregistryserver.

  • sudo docker tag eclipse_for_foo http://mydockerregistry.foo.com:8080/eclipse_for_foo eclipse_for_foo

    Tag the local image/prepare it for pushing to registry server.

  • sudo docker push http://mydockerregistry.foo.com:8080/eclipse_for_foo

    Push the image to registry server.

Pulling the image from the registry server

Now that the image has been pushed to the registry server it can be pulled and run by others users using the docker pull and/or run commands:

sudo docker pull http://mydockerregistry.foo.com:8080/eclipse_for_foo

sudo docker run --name eclipse_for_foo -it -v /home/joemat/development:/share/development -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix http://mydockerregistry.foo.com:8080/eclipse_for_foo

Once the user has created the container, (s)he can restart the container with:

sudo docker start -i eclipse_for_foo

Mission accomplished :)

For more and detailed information see The Docker User's Guide and The Docker Book.

Sonntag, 15. Februar 2015

A tiny tool to install eclipse + plugins via commandline

Base on the information from my previous post I wrote a little bash script to simplify the eclipse installation via commandline.

Find the sources in the subdir eclipse_install_tools of my GitHub project.

A fresh eclipse installation with checkstyle and findbugs plugins would be created with the three commandline calls:

  ./install_eclipse.sh -t /home/tools/luna
  ./install_eclipse.sh -t /home/tools/luna -p findbugs 
  ./install_eclipse.sh -t /home/tools/luna -p checkstyle 

The script has been tested on Linux, but should also work on Windows when a bash (e.g. win-bash) is installed and the URL to the eclipse installation file is adjusted.

The script is also used in the dockerfile to create a docker image for a Eclipse RCP/RAP development environment.

Donnerstag, 12. Februar 2015

Installing Eclipse Plugins via commandline

I just learned, that it's possible to install eclipse plugins via a command line call.

All you need is an installed eclipse and the URL of the update site, e.g. http://findbugs.cs.umd.edu/eclipse which is the update site of the Eclipse FindBugs Plugin.

First call Eclipse to list the available features:


$ eclipse -clean -purgeHistory \
 -application org.eclipse.equinox.p2.director \
 -noSplash \
 -list -repository http://findbugs.cs.umd.edu/eclipse
edu.umd.cs.findbugs.plugin.eclipse=2.0.3.20131122-15027
edu.umd.cs.findbugs.plugin.eclipse=3.0.0.20140706-2cfb468
edu.umd.cs.findbugs.plugin.eclipse.feature.group=2.0.3.20131122-15027
edu.umd.cs.findbugs.plugin.eclipse.feature.group=3.0.0.20140706-2cfb468
edu.umd.cs.findbugs.plugin.eclipse.feature.jar=2.0.3.20131122-15027
edu.umd.cs.findbugs.plugin.eclipse.feature.jar=3.0.0.20140706-2cfb468
http://findbugs.cs.umd.edu/eclipse/site.xml.FindBugs=1.0.0.7_-5cJBiB97E7AK9MAM3DUQLSEI
Operation completed in 3411 ms.


Then tell Eclipse to install the features you want to use:
$ eclipse -clean -purgeHistory\
 -application org.eclipse.equinox.p2.director \
 -noSplash \
 -repository http://findbugs.cs.umd.edu/eclipse \
 -installIUs edu.umd.cs.findbugs.plugin.eclipse
Installing edu.umd.cs.findbugs.plugin.eclipse 3.0.0.20140706-2cfb468.

That's all :)

For a complete list and description of parameters see the chapter in the eclipse online help.

I'm currently using this feature to build a docker container for a complete Java development environment.