Modularity by Paul Bakker

In the LTS webcast on Januari 29th I discussed why modularity is important, and how OSGi can
be used to create modular cloud application with Java.

The case for modularity

Modularity is far fr om a new concept. In the sixties the concept of modular architecture was
already discussed. Still today, most code bases turn out to become inflexible, monolithic beasts
over time. Apparently modularity isn’t easy. But why should we care? What would true modularity
bring us, and why is this becoming a more relevant topic recently?

We are building more and more complex software. At the same time we have been pushing
strongly to more agile models, wh ere changes to requirements are embraced. This is obviously
a good thing, but our code bases must be able to deal with increased complexity and potentially
high impact changes continuously as well. If you have worked on the same code base for more
than a year or two, you probably recognize that after a while the code starts to become more and
more coupled together. Because of that, it becomes harder to make changes to that code base
over time. This is going to hurt the agility of the team, and probably your mood as well.

Modularity is the solution to this problem. By splitting a system into many very small pieces, it
becomes much easier to maintain, extend or completely replace those pieces. In a modular
system, you don’t have think about the consequences of code changes for the whole system.
Instead, code changes are limited to individual modules.

OSGi is the only mature modularity framework for Java. OSGi has been around for about ten
years, and is being used in many different fields, ranging from embedded systems, to application
servers and large scale cloud applications. Of course modularity is first and foremost an
architectural principle. It is possible, to some extend, to create a modular application without
using OSGi, or any other modular runtime. Build time tools such as Maven can be used to create
different modules within a project. Without a modular runtime however, a modular application
looses all modularity again after deployment, and you’re back at a flat classpath and all problems
associated with it. Because of this, it is very difficult to achieve true modularity based just on
build tools.

Modularity with OSGi

In the past OSGi has had the name to be complex and difficult to use. With today’s tools and
frameworks this is far from true. Bndtools, an open source plugin for Eclipse, makes OSGi
development fast and easy. One of the nicest features during development is the extremely fast
codesavetest cycle, as can be seen in the following video:
https://vimeo.com/user17212182/review/83374139/1cea4e1d61. Basically you get a coding
experience in Java similar to the coding experience in scripting languages such as Ruby and
JavaScript, in the way that code changes are immediately visible without waiting for slow builds.
Modularity in OSGi is achieved at different levels. At the lowest level OSGi is all about class
loading. Only classes and interfaces explicitly exported by a bundle can be loaded (imported) by
other bundles. From the other side, each external package that a bundle uses must be explicitly
imported. This makes it possible to hide implementation entirely, which is an important step
towards modularity. Even more important are μServices. A service oriented architecture
decouples parts of a system by using interfaces. Note that a service in OSGi is extremely
lightweight, and doesn’t have any runtime overhead. A bundle can register a service to the
service registry. Another bundle can lookup services from the registry by it’s interface. This way
the only coupling between the components is that interface. You can change or completely
replace the implementation without breaking existing code. In theory this isn’t so different from
dependency injection with other frameworks, but just by itself DI is not sufficient. Using
μServices is really easy, there are several frameworks available that make consuming and
registering services trivial. The following code example shows an OSGi service that exposes a
RESTful web service using Amdatu, and uses another service as well.

@Path(“products")
@Component
public class ProductsResource {
      @ServiceDependency
   private volatile ProductService productService;

      @GET
   @Produces(MediaType.APPLICATION_JSON)
      @Path("categories")
   public List<String> listCategories() {
         return productService.listCategories();
}


In traditional enterprise application architecture systems are commonly described as layers; a UI
layer, a business layer and data/integration layer (and often more). This is a good start, but we
need a much more fine grained level of modularity. As a rule of thumb you can often take one
functional area, e.g. “products”, together with a technology layer, and make that bundle. With that
example we could have the following bundles: progress.api, progress.rest, progress.mongo and
progress.events. Services should do only a single thing, and do that single thing well. Services
may use other services and when kept small, they become reusable units within the system.

modularity_paul_bakker_2.png

Services can use other services


Taking it to the cloud

Now that we have seen how to develop an application based on services, it’s time to discuss
deployment. OSGi can of course be deployed in a variety of ways. In our deployment scenarios
we want to leverage the cloud, first and foremost for auto scaling, but also because the reduced
required management. Looking at the current Platform as a Service (PaaS) providers, there is
not really an attractive option yet. All providers either use proprietary APIs, use inflexible
non-modular deployments or are too limited on what you can and cannot run. The PaaS
landscape is starting to look better though, so this might be different in the future, but we’re not
quite there yet.

Instead of using an existing PaaS we can choose to roll our own solution based on Infrastructure
as a Service. The downside of doing so is more manual setup. The good news is that this can
be very easy for OSGi applications. We start our IaaS nodes with an almost standard Linux
image. The only additional software installed is a barebones Apache Felix OSGi runtime with the
Apache ACE management agent. When the node starts, it starts the OSGi runtime as well, but it
doesn’t contain any bundles yet. To provision nodes with our software, we use Apache ACE.

Apache ACE is an open source provisioning server, focussed on deploying OSGi applications.
Instead of uploading software releases directly onto our servers, we upload a release to Apache
ACE. This basically means uploading the bundle jar files to Apache ACE. Apache ACE will than
distribute those bundles to all the servers. It’s even better for incremental releases. When for
example a single bundle is changed for a bug fix, we only upload this single bundle to Apache
ACE. This single bundle update will than be distributed to all servers. The servers don’t have to
shut down, they will install the new bundle while running. Deploying updates like this takes less
than a second, bringing server downtime down to almost nothing.

When a new node starts, it will automatically be registered to Apache ACE and receive software.
Within a minute a new node can start serving requests. This makes it extremely easy and quick
to add new machines to a cluster. Given this setup, we can setup auto scaling and failover.
Depending on the cloud provider you are using there are different APIs to configure auto scaling.
On Amazon AWS this is called AWS AutoScaling, which offers both web services and a
command line tool. Using AWS AutoScaling you can define a cluster and configure how many
nodes a cluster should contain. You can also configure all sorts of triggers for when new nodes
should be added to the cluster, or when nodes should be shut down. AutoScaling will
automatically start and stop nodes, and because nodes automatically receive software from
Apache ACE, they will become active without any manual steps. Failover comes for free in this
setup. AWS AutoScaling can be configured with a health check. When the health check fails on
a node, it can be automatically replaced by a new node.

One of the great things about this setup is the idea of disposable nodes. We never have to
maintain our servers, application server or even the IaaS image we are using. Instead, when we
have a software update, we simply upload it to Apache ACE. When a node starts to misbehave
because of either a software or hardware problem, we simply replace it with a new node. This
greatly reduces server maintenance costs and reduces downtime.

Uploading releases to Apache ACE can be completely scripted from a Continuous Integration
server. This way we remove all manual steps from the deployment process, which makes
deployments fast, easy and low risk.

In a more traditional infrastructure OSGi applications can be deployed to application servers as
well. Some application servers offer native support for OSGi, for others you can wrap OSGi in,
for example, a Servlet container. Alternatively you could look at Apache Karaf, which is an
application server specialized at OSGi.

Learning more

There are plenty of resources available to learn more about OSGi to get you started quickly. First
of all there is the book written by me and Bert Ertman: “Building Modular Cloud Apps with OSGi
from O’Reilly. There are also tutorial video’s available on amdatu.org and my blog.

modularity_book_cover_pb.jpg

Paul Bakker
Luminis Technologies
@pbakker

Comments

Not to be published