Chapter 2. Developing a simple OSGi-based web application

This chapter covers

  • Setting up an enterprise OSGi container
  • Developing a simple OSGi-based web application
  • Wiring components together with Blueprint
  • Using JNDI to integrate enterprise OSGi and Java EE code

The web is one of the most fundamental parts of enterprise Java programming, providing a frontend for almost every enterprise application. At the end of the chapter 1, we mentioned that a trivial Hello World application didn’t make much sense when we were talking about enterprise OSGi, because enterprise applications are by definition nontrivial. But that doesn’t mean that writing them has to be complicated or difficult! In this chapter, we’ll get you going with a simple web application, and then we’ll move on to discuss the essential glue of enterprise OSGi: Blueprint dependency injection. Think of it as Hello World Wide Web.

We’re sure you’re eager to get going playing with real code, so we’ll begin by introducing our sample application and letting you get your hands dirty with a handy development sandbox.

2.1. The development sandbox

In the succeeding chapters, we’ll build up a small sample enterprise application, the Fancy Foods web store. By the end of the book, the Fancy Foods application will have a web frontend, a database backend, and transactional purchasing. Its modules will be wired to one another using dependency injection, and packaged together as an installable archive.

Before you get started writing this application, you’ll need a way to run it. Enterprise OSGi is a programming model, not a product, so there are lots of ways of running an enterprise OSGi application. Several open source and commercial application servers support enterprise OSGi, and we’ll discuss some of the available platforms in chapter 13. As a starting point, the Apache Aries project provides a simple runtime with their samples. We think this is one of the best ways of getting going with enterprise OSGi.

2.1.1. Introducing Apache Aries

The open source community has been quick to respond to the OSGi Enterprise Specification with implementations. (At least some of these implementations were in place before the Enterprise Specification was released, and the implementation experience guided the final shape of the specification.) Open source enterprise OSGi implementations are available from both the Eclipse Foundation and the Apache Source Foundation, and many other implementations are available elsewhere. The primary enterprise OSGi project in Eclipse is called Eclipse Gemini, and in Apache the project is known as Apache Aries. These projects have the express aim of providing a set of reusable OSGi components that can be used as the basis of an enterprise OSGi programming model.

Aries and the OSGI Enterprise Specification

Apache Aries (see figure 2.1) also provides some extensions to what’s included in the OSGi Enterprise Specification, and at least some of these extensions have been wrapped back into release 5. For example, Aries provides fuller container management of persistence and transactions. It also provides a new application-level (rather than bundle-level) granularity that wasn’t available in the first release of the enterprise OSGi specification.

Figure 2.1. The elements of Apache Aries. Aries includes a Blueprint implementation, OSGi integrations for JPA, JTA, and the web, application packaging, and JMX management. Blueprint is at the heart of a lot of the Aries function.

These extensions are tremendously useful and make what’s provided by enterprise OSGi map more closely to what’s in JEE. Apache Aries also has a number of other benefits that make it a nice place to start learning enterprise OSGi. It has a helpful users’ mailing list and an active development community. It can be consumed in a number of forms. The JARs can be downloaded as-is and dropped into any OSGi framework. Aries is also integrated into several existing application servers. Both Apache Geronimo and IBM’s WebSphere Application Server provide an OSGi programming model based on Aries. We’ll focus on Apache Aries and a number of its sibling Apache projects in our examples in this book. It’s not necessary to use an Apache stack to use enterprise OSGi. You can replace parts of the stack with other implementations, or you can use an entirely Eclipse stack, or you can opt for one of the commercial products, such as the WebSphere Application Server.

2.1.2. My first enterprise OSGi runtime

Apache Aries by itself isn’t enough to run enterprise applications; there’s no web container, database implementation, or OSGi framework. But it’s fairly straightforward to add these things to Aries, and the result is a handy little sandbox environment. Aries has already done this, providing an assembly for the Aries samples, and this runtime can be reused for your own applications.

Begin by downloading the source zip for the latest release of the Aries samples from http://aries.apache.org/downloads/currentrelease.html. The samples are listed at the top of the page, and you want the main samples download, a zip that contains the source for all the samples. Unzip and navigate down one level in the directory structure. You’ll find the source code and build files for all the Aries samples. Navigate to blog/blog-assembly. Finally, use Maven to build the assembly:

mvn install

This build isn’t building any samples, just preparing a runtime environment for the samples to run in. It downloads the binaries for the latest Aries release and all their dependencies. It also includes Pax Web for running OSGi web applications. When the build has completed, have a look in the target directory. You should see lots of Aries JARs, and also a few Derby, OpenJPA, and Geronimo JARs. Perhaps most importantly, there’s a single JAR containing an OSGi container. See figure 2.2.

Figure 2.2. As well as Apache Aries components, the Aries sandbox stack includes an OSGi framework, a database, web container, JPA and JTA implementations, and a few pieces of the Geronimo application server.

What do all those JARs do?

If you count the JARs in the target directory, you may be surprised by how many there are. Does it take 35 JARs to run an OSGi web application? No. You could get away with a handful of bundles, but then you’d need to download a few more to get the Blueprint dependency injection going, and then a few more once you start playing with persistence in chapter 3. We thought it was probably easier to download everything at once!

If you’re the sort of person who likes to take everything apart and then put it back together again, we’ve got a detailed guide to what’s what in the Aries sandbox in appendix B.

Launch the OSGi framework using the OSGi JAR:

cd target
java -jar org.eclipse.osgi-3.7.0.v20110613.jar -console

(Depending on which release you’re using, the version of the OSGi JAR may be different.) The -console argument is important, because it brings up the OSGi console that allows you to see what OSGi bundles are active and start, stop, and install new bundles.

Type ss to see a list of all the bundles in the framework (see figure 2.3). The ss stands for “short status” and provides a high-level view of the bundles available in the framework. Each bundle should be in ACTIVE state. That’s it! Your new enterprise OSGi container is now ready to run applications.

Figure 2.3. The output of the ss command, showing all the bundles in the OSGi framework

It’s probably obvious that this runtime isn’t suitable for use in production. Support for administering applications is limited, and there’s no support at all for security, load balancing, or failover. Don’t worry, we’ll cover a variety of production-worthy options in chapter 13. In the meantime, the sandbox is a good environment for playing with various bits of enterprise OSGi. We’ll start with the public face of almost any enterprise application, the web frontend.

OSGi consoles

The OSGi console you’re using now is a standard Equinox OSGi console. There’s nothing specific to Apache Aries in the user interface. The command line environment of an OSGi console can feel intimidating at first, but you won’t need to know many commands other than ss to show the bundles.

2.2. Writing an OSGi web application

In Java EE the servlet and JavaServer Pages models have provided the basic building blocks for Java web applications for many years. OSGi web applications are a standardized OSGi version of Java EE web applications. An OSGi web bundle is similar to a Java EE WAR, except that it also gets the benefits of operating in an OSGi framework. Enterprise OSGi web bundles are known as WABs. (In contrast to WARs, which are Web Archives, WABs are Web Application Bundles.)

2.2.1. Building a simple OSGi web application bundle

Let’s give WABs a try. You can use your favorite OSGi or Java EE development tools. All you need for now is the ability to compile Java code and build JARs. (We’ll cover specialist enterprise OSGi development tools in chapter 8. Feel free to peek ahead now if you’re struggling to make OSGi JARs.)

Wab Layouts

The simplest WAB contains three files. These are a Java servlet class, a JAR manifest, and a web deployment descriptor. Figure 2.4 shows how they’re laid out in the WAB. Like WARs, WABs are a special kind of JAR. Unlike WARs, the enterprise OSGi specification doesn’t require a particular file extension. As a result, WAB files may have any extension, but typically use .jar or .war to avoid confusion. We’ll call our WAB fancyfoods.web_1.0.0.jar.

Figure 2.4. The layout of the fancyfoods.web JAR. All code lives in WEB-INF/classes. The web container looks at WEB-INF/web.xml to find out what servlets are provided by the bundle. Finally, the standard JAR manifest, META-INF/MANIFEST.MF includes important extra metadata for the OSGi container.

Web Deployment Descriptors

Let’s start with the deployment descriptor. Listing 2.1 shows the web.xml file for the web application. It’s a typical web.xml file whose syntax will be reassuringly familiar to everyone who has developed Java EE web applications. The web application has one servlet, whose class is fancyfoods.web.SayHello.

Listing 2.1. The WEB-INF/web.xml file

A Simple Servlet

The servlet class SayHello is also exactly the same as it would be in a WAR. Listing 2.2 shows the source. There’s one method, which—unsurprisingly—issues a greeting to a user.

Listing 2.2. The SayHello.java file

So far, so familiar. It’s perhaps a bit anticlimactic that writing a WAB is so similar to writing a WAR in some respects, but this is one of the strengths of the enterprise OSGi programming model—it’s like existing programming models, only better. The differences between WABs and WARs start to become obvious when you look at the manifest file.

A Wab Manifest

The final file needed in your WAB is the bundle manifest. Every JAR has a MANIFEST.MF file, but an OSGi bundle’s manifest has extra headers, such as the symbolic name of the bundle and the bundle’s version.

Listing 2.3. The MANIFEST.MF file
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-SymbolicName: fancyfoods.web
Bundle-Version: 1.0.0
Bundle-ClassPath: WEB-INF/classes
Web-ContextPath: /fancyfoods.web
Import-Package: javax.servlet.http;version="[2.5,3.0)",
 javax.servlet;version="[2.5,3.0)"

To be consistent with the layout of a WAR, the class files for fancyfoods.web have been packaged in the WEB-INF/classes folder. But there’s no need for this. Classes can live anywhere in an OSGi bundle, or even be spread across multiple locations. If the class files aren’t directly in the root directory, the classpath needs to be specified in the manifest:

Bundle-ClassPath: WEB-INF/classes

Packages used by the servlet that aren’t in the servlet’s own bundle must be explicitly imported in the manifest. Otherwise, they won’t be visible. The exception is that there’s no need to import the core Java language classes, java.*, which are implicitly imported. Bundle wiring rules are a bit different for the java.* packages, which must come from the core Java runtime for security and for compatibility with the virtual machine. Imagine if someone could replace the implementation of String or Integer!

In the case of the web bundle, this means the javax.servlet and javax.servlet.http packages are imported. The servlet is expected to work with any version of javax.servlet with version 2.5 or higher, up to but not including version 3.0.

Import-Package: javax.servlet.http;version="[2.5,3.0)",
 javax.servlet;version="[2.5,3.0)"
Warning: What About Servlet 3.0?

The meaning of the version range "[2.5, 3.0)" isn’t entirely straightforward. You’re mostly right if you assume that the 2.5 part implies version 2.5 of the servlet specification. But 3.0 definitely doesn’t mean version 3.0 of the servlet specification! Remember, OSGi versions are semantic package versions, not marketing or specification versions. Servlet 3.0 is backwards compatible with servlet 2.5, and so the package versions for servlet 3.0 won’t be versioned at 3.0. Version 3.0 of the servlet packages would be some change to the servlet specification so radical that the interfaces were no longer backwards compatible. The reason the bottom range starts at 2.5 and not 1.0 is that when the WAB specification was written, the current version was 2.5, and so 2.5 seemed like a logical starting point. Unfortunately, some application servers have deviated from the semantic version and use the package version 3.0 for the servlet 3.0 specification, which doesn’t help!

The manifest includes one final header that’s specific to WABs and defines the web context root. This header is required for a bundle to be recognized as a WAB. Many enterprise OSGi implementations also allow the context root to be changed after deployment:

Web-ContextPath: /fancyfoods.web

Build these three files into a JAR, and the web application is ready to try out!

2.2.2. Deploying and testing

Because OSGi is so dynamic, testing OSGi bundles is pretty easy. The same bundle can be loaded repeatedly without having to stop or start anything else. If you’re as prone to typos as the authors, you’ll find this extremely handy.

The Load Directory

The Apache Aries runtime you assembled earlier includes Felix File Install, which provides a simple mechanism for discovering and starting new applications. The target directory includes a folder called load (if the load directory isn’t there, you can add it yourself). Any OSGi bundles copied into this directory will automatically be started.

To try this out, start the Aries runtime using

java -jar org.eclipse.osgi-3.7.0.v20110613.jar -console -clean

Type ss to see the list of installed bundles. Now copy the web bundle you’ve built into the load directory. You’ll see a bunch of output scroll by in the OSGi console. Type ss again. You’ll see a new bundle is listed, the fancyfoods.web bundle you copied into the load directory. This new bundle should be in ACTIVE state.

All that remains now is to try it out. Point a browser at http://localhost:8080/fancyfoods.web/SayHello. You’ll see more debug output scroll by in the OSGi console, and the browser should display a response like the one shown in figure 2.5.

Figure 2.5. The web application in action

What if nothing happens?

Our sandbox uses Apache Felix’s File Install bundle to install bundles that we drop into the load directory. This is quick and easy, but it’s also quiet when there’s a failure. It’s quite common for nothing to be output at all. If you don’t see anything happen when you drop your bundle into the load directory, don’t panic. You can use one of the other Equinox console commands to find out what’s gone wrong.

The install command allows users to install a bundle from a particular URL. In your case, you want to point at the fancyfoods.web_1.0.0.jar, and need to run the following command:

install file://<path_to_file>/fancyfoods.web_1.0.0.jar

This command will almost certainly generate a useful error. Typically the error will indicate that the bundle’s manifest is malformed, often because there aren’t any " characters around a version range. Whatever the error is, it should provide enough information to help you fix the problem and continue on with the example.

2.2.3. A familiar feeling—and important differences

Even with the extra headers in the manifest, the web bundle you’ve written looks a lot like a conventional Java EE WAR. In fact, it’s so similar that you could probably deploy it as a WAR in a Java EE application. What’s different about it, then? A WAB is a bundle, and not just a normal JAR, which means it has some new behaviors.

WAR to WAB conversion

The structure of WARs and WABs is similar enough that the OSGi Enterprise Specification supports automatic conversion of WARs to WABs at deploy time. A bundle symbolic name and package imports are automatically generated. This can be convenient when doing an initial migration from Java EE to enterprise OSGi, but in general it’s better to write web applications as WABs. This ensures the bundle has a proper, well-known, symbolic name, and it also allows package imports to be versioned. Versioning package imports is always a good idea. The WAB format also provides a convenient mechanism for setting the web context root.

Package Privacy

What are the implications of being an OSGi bundle? The biggest implication is what can’t be seen—nothing outside the fancyfoods.web bundle (see figure 2.6) can see the SayHello class, because it’s not exported. This cozy privacy is exactly what you want, because there’s no reason for any code outside your bundle (except for perhaps the web container, which can use the OSGi API) to be messing around directly with your servlet class. If you did want to make the SayHello class externally accessible for some reason, all that would be required is to add a package export of the fancyfoods.web package. But you’d probably want to consider your design carefully before doing this. Could the shared code be externalized to a utility bundle instead?

Figure 2.6. The class space of the fancyfoods.web bundle. It doesn’t have any public packages. To confirm which bundle exports the javax.servlet package, type packages javax.servlet in your OSGi console, or bundle fancyfoods.web to see where all of the packages used by fancyfoods.web come from.

Although package privacy is a good thing, not all Java code can cope with it. Some existing libraries, particularly ones that use reflection to load classes, may not work properly. We’ll discuss strategies for handling awkward existing libraries in chapter 12.

Explicit Dependencies

Being a bundle has a second implication, which is that all the packages required by SayHello must be explicitly imported in the manifest. If you’re just getting used to OSGi, this can seem like an unnecessary—and annoying—extra step.

Let’s step back and think about how Java handles package imports for classes. If you were writing a Java class, you’d always import the packages you were using, rather than expecting the compiler to choose a class with the right name from a random package. Some class names are unique, and you’d probably end up with the right one, but other class names are not at all unique. Imagine how horrible it would be if your class could end up running against any class called Constants, for example.

You might also end up with the opposite problem—instead of a class name that was too common, you could be trying to use a class that didn’t exist at all. If the package you needed didn’t exist at all, you’d expect an error at compile time. You certainly wouldn’t want the compilation to limp along, claim success, and produce a class that half-worked.

Luckily, the Java compiler doesn’t do this. If your declared dependencies aren’t present, the compilation will fail quickly. At runtime, on the other hand, you’re in a situation which is similar to the undeclared dependencies. You have to wait until your class is invoked to discover its dependencies are missing. You won’t ever end up running against a class from a totally different package to the one you expected, but you could end up running with a class of a different version, with totally different methods and behaviors.

Explicitly declaring the dependency on the javax.servlet and javax.servlet .http packages ensures the fancyfoods.web bundle won’t run in a container that doesn’t support servlets. Better yet, it won’t even run in a container that supports an obsolete version of the servlet specification. To try this out, go to the OSGi console for the Aries runtime. At the prompt, use the packages command to see which bundles import and export the javax.servlet package:

osgi> packages javax.servlet

The response should be something like that shown in figure 2.7.

Figure 2.7. The OSGi console can provide the list of bundles providing and consuming the javax.servlet package.

The output shows that the org.apache.geronimo.specs.geronimo-servlet_2.5_spec bundle exports the javax.servlet package, and five bundles import it, including fancyfoods.web.

Does it matter who provides the servlet package?

Depending on which version of the Aries sample assembly you’re using, your console output may be different from what’s shown in figure 2.7. In particular, it may be a different bundle that provides the javax.servlet package. Declaring dependencies on packages, rather than bundles, allows more deployment flexibility. In this case, the Aries team has taken advantage of that flexibility by swapping the web container implementation.

What would happen if the Aries assembly hadn’t included the Geronimo servlet API JAR? Quit the OSGi console, and move the Geronimo servlet bundle (geronimo-servlet*jar) out of the target directory. (Don’t lose it though!) Restart the OSGi console, and type ss to see the list of bundles. You’ll see a number of bundles, including fancyfoods.web, are in the INSTALLED state instead of the ACTIVE state. This means the bundles couldn’t be resolved or started because some dependencies were missing. Make a note of the bundle identifier to the left of fancyfoods.web in the bundle list. Try starting the bundle to see what happens. See figure 2.8.

Figure 2.8. If the Geronimo servlet bundle is removed from the runtime, the fancyfoods.web bundle can’t be started, because no servlet API is present.

Because your assembly no longer has servlet support, fancyfoods.web won’t start. The bundle definitely wouldn’t work properly if the code inside it was to run, so not starting is the right behavior. Don’t forget to put the Geronimo servlet bundle back into the target directory before you try to use the web application again. (The authors forgot to do this on several occasions, and were confused each time.)

Fragments

OSGi bundles have a third advantage over normal WARs. OSGi is all about modularity, and so OSGi bundles can themselves have modular extensions, known as fragments. Fragments are extensions to bundles that attach to a host bundle and act in almost every way as if they were part of the host. They allow bundles to be customized depending on their environment. For example, translated resource files can be packaged up by themselves into a fragment and only shipped if needed. Fragments can also be used to add platform-specific code to a generic host.

2.2.4. Spicing things up with fragments

How would a fragment work with your little application? The first version of the fancyfoods.web application is only intended to work in English, but if the business takes off, it will expand into other markets. The first step in internationalizing fancyfoods.web is to externalize the strings in SayHello.java. Write a properties file called fancyfoods/web/messages.properties with the following content:

SayHello.hello=Hello valued customer!

The new doGet method looks like the following.

Listing 2.4. The doGet method with message translation

Bundles, bundles, or bundles?

You’ve got a few different kinds of bundles floating around at the moment, but the resource bundles here have nothing to do with OSGi bundles—they’re ordinary Java resource bundles.

If you build the bundle and test the web page, it will work exactly as it did before. This is reassuring if you’re browsing in English, but not ideal if you’re browsing in French. To try it out: change your web browser’s preferred language to French. (If you don’t want to do that, you can hardcode the locale in the getString() call in SayHello .java.) Most pages you browse to, like Google, for example, will show French text. But if you reload the Fancy Foods web page, the greeting is disappointingly English. To get the Fancy Foods page to display in French, you need to provide some French translations—obviously.

To be accessible to the SayHello class, the properties files need to be loaded by the same classloader—which (mostly) means they need to be in the same bundle. But rebuilding JARs is no fun, and you definitely don’t want to be repackaging your existing code every time you have a new translation. You want to be able to easily drop in support for other languages in the future.

Resource loading between bundles

We’ve simplified our discussion of resource loading slightly. It’s possible to load resources from other bundles, but it’s ugly. The package containing the resource must be exported by the providing bundle and imported by the consuming bundle. To avoid clashes with packages in the consuming bundle, the consuming bundle shouldn’t export the package it’s attempting to import. Having trouble following? You won’t be the only one! We’ve seen this pattern used, but we definitely don’t recommend it.

Luckily, this is the sort of job for which OSGi fragments are perfect. OSGi fragments are a bit like OSGi bundles. But instead of having their own lifecycle and classloader, they attach to a host bundle. They share the host’s classloader and behave in almost every way as if they’re part of the parent (see figure 2.9). But they can be installed and uninstalled independently of the host.

Figure 2.9. OSGi fragments attach to a parent bundle and share its classloader.

In this case, a translation fragment can be built and attached to fancyfoods.web. To provide the translations, you’ll need a new fragment JAR. All it needs inside it is a manifest and a properties file (see figure 2.10).

Figure 2.10. The layout of the fancyfoods.web.nls.fr fragment

The French language properties file, messages_fr.properties, might read like this:

SayHello.hello=Bienvenue aux Aliments de Fantaisie!

The MANIFEST.MF is similar to a bundle manifest, but it has an extra header that identifies the host of the fragment—fancyfoods.web in this case. It’s a good idea to also specify a minimum version of the host bundle, to ensure compatibility:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: French language resources
Bundle-SymbolicName: fancyfoods.web.nls.fr
Bundle-Version: 1.0.0
Fragment-Host: fancyfoods.web;bundle-version="[1.0.0,2.0.0)"

Build the fragment into a JAR, fancyfoods.web.nls.fr_1.0.0.jar. After the fragment is built, you can drop it into the load directory of your running framework. Type ss and you’ll see your new fragment included in the list of bundles. Fragments can’t be started and stopped like bundles, so the fragment will be shown as INSTALLED. Refresh the web bundle with refresh [web bundle number] and the fragment will attach to the bundle and move to the RESOLVED state.

Check the web page again, and the greeting should be shown in French. Delete the fancyfoods.web.nls.fr fragment JAR from the load directory, and try the web page again—back to English (figure 2.11)!

Figure 2.11. The Fancy Foods web page, before and after deleting the French-language translations from the load directory

Although internationalization is the most popular use for fragments, it’s not the only one. Anything can be put into a fragment, including Java classes. But including classes in fragments for pluggability isn’t the best implementation. OSGi provides higher-level ways of achieving pluggability through services.

2.3. Decoupling with dependency injection

Despite working in two languages, the web application you’ve written is pretty limited so far. Not only does it not do anything beyond issuing a greeting, it doesn’t even say hello in a particularly dynamic way. What if you wanted to change the greeting depending on the time of day, or take the opportunity to highlight items which were on special offer?

All of this logic could be coded into SayHello.java, but that’s not a flexible or dynamic architecture. (Remember—recompiling is no fun.) What you’d like is the ability to publish, or register, extra content providers that are made available to the web page without it having to hardcode lookups.

As you’ve seen, code can be separated out into fragments that are installed independently. This is slightly more modular and dynamic than lumping everything into one JAR. But a fragment’s host must be specified at build time, which isn’t modular. As you’ve seen, they also aren’t that dynamic, because the host bundle needs to be refreshed to attach and detach them. This makes them a useful mechanism for extending or modifying bundles, but not great for reuse or dynamic behavior changes.

If you’re familiar with OSGi services, you might be thinking right now that OSGi services are an excellent way of providing chunks of function that can appear and disappear dynamically. A service can be used without ever specifying where the service comes from or who provides it, which means service-oriented code remains loosely coupled. But the code for looking up services can be a bit verbose. The code for managing services that might appear and disappear dynamically is even more long-winded.

If you’re not coming from the world of OSGi, you’re probably thinking of a different solution instead—dependency injection.

2.3.1. Inversion of control

Inversion of control is an elegant programming pattern where required services are supplied rather than looked up. This is often known as dependency injection, and occasionally as the Hollywood principle (“don’t call us, we’ll call you”).

In general, dependency injection refers to a dependency injection container that wires together managed objects, injecting the dependencies into an object. The dependency injection container manages the lifecycle of objects, typically supporting callbacks for initialization and destruction. Figure 2.12 shows the differences between dependency lookup and dependency injection.

Figure 2.12. Normal control and inversion of control. In the normal case, the consumer looks up a service it requires. In the inversion of control model, also known as dependency injection, the consumer is automatically given the service. An application container manages passing the service to the consumer.

Dependency injection allows applications to be configured differently in different environments. For example, it’s often useful to do unit testing with a lightweight version of a required component, possibly even a mocked implementation. Furthermore, as the business expands or requirements change, a different version of the component can be wired in, one that’s more available, or scales better, or one that has better performance.

Dependency injection extends the loose coupling model to completely externalize the couplings from the code. Instructions for wiring components together are usually stored in XML metadata that can be changed relatively easily, without a recompile.

Who injects the dependency?

One of the nice things about dependency injection frameworks is how easy it is to not use them—when it suits you. When code constructs all its own dependencies, it can be difficult to bypass that code and run against stubs for testing. If dependencies are passed in, on the other hand, all that’s required is for your test code to call a setter with a mock object of your choice.

2.3.2. Introducing the Blueprint service

Enterprise OSGi provides a simple way of accessing services declaratively, known as Blueprint. Blueprint supports inversion of control; a component’s dependencies are injected into it rather than being looked up by it. Like almost everything in the enterprise OSGi programming model, Blueprint itself is implemented as a service. (It’s a service for registering and injecting services.)

Dependency Injection and OSGI

Dependency injection is useful everywhere, but has special benefits in an OSGi environment. Because the OSGi Service Registry is such a dynamic environment, it’s difficult to correctly write a bundle that makes use of a service in a safe way, monitoring its lifecycle and finding an appropriate replacement. Using and providing services becomes even more difficult when an implementation depends upon more than one service, with a user having to write some complex thread-safe code. Using dependency injection eliminates this complexity by managing the lifecycle of the services exposed and consumed by a bundle.

In Apache Aries, Blueprint is used as a core part of the OSGi programming model. It’s used not only to provide a dependency injection model, but it’s also the integration point for many enterprise technologies and declarative qualities of service, such as managed JPA and container managed transactions. We’ll explain more about JPA and JTA in the next chapter and discuss alternative OSGi dependency injection frameworks in section 6.2.

2.3.3. Coupling two components with Blueprint

One thing that would enhance the Fancy Foods web page is a listing of special offers. The Fancy Foods shop will have several departments, each with their own criteria for what goes on offer. For example, prices of fresh fruit and vegetables will vary a lot depending on the season and the weather. (In general, fruit prices and vegetable prices move in opposite directions. Summer is best for most fruits, whereas many vegetables are at their best in winter. This is partly why Brussels sprouts are so popular for Christmas dinner.) Other products don’t have the same seasonal price fluctuations, but sell better at certain times of the year. For example, it makes sense to highlight premium chocolates on the front page around Valentine’s Day, bulk candies for Halloween, and pickled onions and organic turkeys before Christmas. Clearly all this logic won’t fit into HelloWorld.java, or even into a single other class.

Let’s define an interface that can be implemented by everything that describes a special offer.

Listing 2.5. The SpecialOffer interface
package fancyfoods.offers;

import fancyfoods.food.Food;

public interface SpecialOffer {

    public Food getOfferFood();

    public String getDescription();
}

It relies on another interface for Food. (This is a food shop—you need food!)

Listing 2.6. The Food interface
package fancyfoods.food;

public interface Food {

    String getName();

    double getPrice();

    int getQuantityInStock();

}

To keep the application modular, these interfaces should go in their own bundle, fancyfoods.api. The fancyfoods.api bundle should export the fancyfoods.offers and fancyfoods.food packages. Keeping shared interfaces in an API bundle is a good OSGi practice; it ensures that only one copy of the interface is on the classpath, and that implementations can be swapped around without worrying about interbundle dependencies.

Exposing Services with Blueprint

What we’d like is a way of registering (or publishing) special offers for each department, without anyone having to maintain a central list of what offers are available. Enter Blueprint. Let’s define a third bundle, fancyfoods.department.chocolate. The manifest for fancyfoods.department.chocolate should declare a dependency on the fancyfoods.offers package:

Manifest-Version: 1.0
Bundle-Blueprint: OSGI-INF/blueprint/*.xml
Bundle-SymbolicName: fancyfoods.department.chocolate
Bundle-Version: 1.0.0
Import-Package: fancyfoods.offers;version="[1.0, 2.0)",
 fancyfoods.food;version="[1.0, 2.0)"
Bundle-ManifestVersion: 2

Next, you need to provide a SpecialOffer implementation:

Listing 2.7. The RomanticChocolateOffer class

There’s no need to export the fancyfoods.department.chocolate.offers package, because nothing else in the application should depend on it. It’s not always possible, but a good goal in application design is to try to divide bundles into those with exported packages but no package dependencies (like fancyfoods.api) and those with imported packages but no externals (like fancyfoods.department.chocolate). A bundle that both exports a lot of packages and imports a lot of packages is highly coupled, and therefore fragile. The ideal dependency graph should look more like a fork than like spaghetti.

How will other code get hold of the RomanticChocolateOffer if the package isn’t exported? To make the class available for dependency injection, you need to provide metadata for the Blueprint service.

Blueprint metadata should live in a folder called OSGI-INF/blueprint. (If for some reason you need to use a different folder, you can point Blueprint to your files by listing them in an optional Bundle-Blueprint: header.) The filename doesn’t matter, but blueprint.xml is a popular choice.

Listing 2.8. A simple blueprint.xml file

The service element declares that a bean should be exposed as an OSGi service, suitable for injection into other Blueprint-managed beans or direct lookup. The bean element describes a managed bean. Blueprint will take care of instantiating the bean and initializing it with required properties. In this case, the RomanticChocolateOffer class doesn’t need any external configuration.

Checking Out the Special Offers

Build the api and chocolate bundles and drop them into the load directory. They’ll start automatically. When you query the chocolate department bundle, you’ll see that it has registered an OSGi service implementing the SpecialOffer interface (figure 2.13).

Figure 2.13. The bundle details for the chocolate bundle show that it registers an OSGi service implementing the SpecialOffer interface. A Blueprint container for this bundle is also registered as a service by the Blueprint implementation.

Annotations and Blueprint

One of the questions we get asked most often about Blueprint is whether it supports annotations. Java 7 and Spring both support annotation-driven injection of dependencies, and an extension for Aries Blueprint does too. Because annotations aren’t part of the Blueprint standard, we won’t cover them; however, we do expect Blueprint annotations to appear in a future release of the enterprise OSGi specification.

Wiring Blueprint Beans Together

Who’s going to use this service? Rather than hooking the service right up to our web bundle, let’s make the dependency chain a bit more interesting by adding an OfferAggregator service. The OfferAggregator takes all the available special offers and works out which ones should be displayed to the user, and in what order. Separating interface from implementation is always a good idea, so the interface should live in the api bundle.

package fancyfoods.offers;

import java.util.List;

public interface CurrentOffers {

    public List<SpecialOffer> getCurrentOffers();

}

Next, we’ll need a new bundle, with another blueprint.xml file. We’ll call this bundle fancyfoods.business and its manifest will need to look something like the following listing.

Listing 2.9. The manifest of the fancyfoods.business bundle
Manifest-Version: 1.0
Bundle-Name: Fancy Foods Business Logic
Bundle-Blueprint: OSGI-INF/blueprint/*.xml
Bundle-SymbolicName: fancyfoods.business
Bundle-Version: 1.0.0
Bundle-ManifestVersion: 2
Import-Package: fancyfoods.food;version="[1.0, 2.0)",
 fancyfoods.offers;version="[1.0, 2.0)"

Like the chocolate department bundle, our bundle will use Blueprint to publish a service. In this case, the bean we’re exposing as a service also has dependencies injected into it.

Listing 2.10. Using Blueprint to inject dependencies

The implementation of the OfferAggregator doesn’t need to instantiate the special offers it’s going to be aggregating—the container injects them. A null check isn’t needed, either, because the container won’t instantiate the class unless its dependencies are available.

Listing 2.11. The OfferAggregator class

If you want to liven things up and make the OfferAggregator work a bit harder, you can register more SpecialOffer implementations. They will all magically appear in the list passed to the OfferAggregator. Figure 2.14 shows how the application architecture would look with an extra cheese department.

Figure 2.14. The backend of the Fancy Foods application. Triangles represent service dependencies, with the broad end of the triangle facing the consumers of the service. There isn’t a direct dependency between the chocolate department and the offer aggregator, but the offer aggregator does consume a service provided by the chocolate department.

The offer aggregator applies the Fancy Foods business logic to the raw list of current offers. Flooding customers with a big long list of promotions will only confuse them, so the aggregator restricts the offer count to four. Customers will probably get more excited by offers for high-cost items, so the aggregator puts those at the top of the list.

References and reference lists

The offer aggregator assumed that multiple offers would be registered, and presenting a list of offers was the right thing to do. For some other types of service, only one is ever required. For example, it would be quite wrong to process credit card payments more than once just because multiple credit card payment processing providers were available! If you only want one implementation, use a <reference> element instead of a <reference-list>. If multiple services are available, the container will supply one of them more or less at random. The order in which services get returned, and therefore the one you get, can be influenced by specifying a ranking in the Blueprint service definition. Finer control can also be achieved using service properties, but for now we’re happy to accept every offer in whatever order it arrives.

Getting into the service-oriented frame of mind

Both of these operations take in a list of special offers and then return a modified list, which is then used as input to the next adjustment. If you’re keen on abstraction, you may already be thinking that each adjustment could be represented as an instance of an OfferListAdjuster interface:

public interface OfferListAdjuster {
    public List<SpecialOffer> adjust(List<SpecialOffer> offers);
}

With this change, the offer aggregator no longer needs to include the code for individual adjustments to the offer list:

public List<SpecialOffer> getCurrentOffers() {
    OfferListAdjuster[] adjusters = new OfferListAdjuster[] {
        new OfferSorter(), new OfferTrimmer() };
    for (OfferListAdjuster adjuster : adjusters) {
        offers = adjuster.adjust(offers);
    }
    return offers;
}

The offer aggregator still has to know what adjustments to make, because the list of adjuster classes is hardcoded. We hope at least some of you have just had a lightbulb moment and realized that it’s not just the list of special offers that could be injected by Blueprint—the list of adjustments could also be injected. This would make the getCurrentOffers() method wonderfully short and generic.

How much should you use Blueprint and OSGi services? With a bit of determination, it’s probably possible to code an entire application without ever constructing one class from another. This doesn’t mean it’s a good idea! Despite Blueprint’s elegance, getting an instance of an interface via Blueprint is a bit more work than just constructing the darn thing. In many cases, the decoupling offered by Blueprint clearly justifies having to write one or two lines of XML, but for some parts of your application, the transparency and concision of close coupling is fine. We’ll discuss architectural patterns for Blueprint and alternate dependency injection frameworks more in chapter 6.

In the case of Fancy Foods, using Blueprint to publish special offers feels right. The list of special offers will be changing regularly, so hardcoding them isn’t a good idea. Each department will know what’s best to offer when—pink chocolates at Valentine’s Day, cheese when it’s approaching its sell-by date. The logic for aggregating the offers, on the other hand, will probably always be centrally managed. You wouldn’t want a chocolate maniac in the chocolate department to be able to publish a rule that always put chocolate offers first in the pile of promotions! Bearing this in mind, even though you could rewrite the offer aggregator to be even more service-oriented, it’s probably not worth it—listing 2.11 is good as it is.

Blueprint beans can be wired together to get some pretty complex behavior. Blueprint isn’t a substitute for good, old-fashioned Java code, but it sometimes can make it an awful lot shorter!

Spring and Blueprint

If you think this looks a lot like Spring Dynamic Modules, you’re right. Blueprint is based on Spring DM. Blueprint does have some areas of technical difference from Spring DM, but the most significant difference is that Blueprint is an open standard.

We’ll be coming back to Blueprint and some of its more advanced features in chapter 6. For now, we’ll show you how to get the web frontend talking to the current offers service.

2.4. Bridging JNDI and OSGi

The final piece is to hook these special offers up to the servlet. There’s a snag, though—most web container implementations aren’t fully integrated with Blueprint (and at the moment, there’s nothing in the specification that says they should be). The problem is that the Blueprint container takes a lot of control over the lifecycle of Blueprint beans—it identifies when the services they require are present, instantiates the classes, and injects any references. Often, tens of classes will be bootstrapped by the Blueprint container in a big chain of managed dependencies. Unfortunately, the web container also takes a lot of control over servlet lifecycles, instantiating them in response to incoming web requests.

It’s easy to see that if something is both a Blueprint bean and a servlet, the Blueprint container and the web container could have a messy tussle over what happens when to the poor servlet, which is caught in the middle. Getting the two to integrate neatly hasn’t been done yet—especially not in a standards-based way.

Unless you’re writing an enterprise OSGi application from scratch, the web container is unlikely to be the only place where the Blueprint injection model doesn’t fit quite right. Luckily, Blueprint has a nice integration with JNDI. Services registered by Blueprint are all available in JNDI, and can be looked up using the interface name (see figure 2.15).

Figure 2.15. The Fancy Foods web bundle accesses the offer aggregator service using JNDI. It has no direct dependency on the chocolate or cheese department services.

Let’s have a look at how you can use JNDI to access your Blueprint service (figure 2.15) by writing another servlet called fancyfoods.web.SayHelloJNDI and putting it into our web bundle. You can see how the doGet method works in the following listing.

Listing 2.12. Adding special offers using JNDI to access OSGi Services
public class SayHelloJNDI extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                        HttpServletResponse response)
                        throws ServletException, IOException {

        PrintWriter html = response.getWriter();
        html.append("<html>");
        html.append("Hello valued customer!<br/>");
        try {
            InitialContext ctx = new InitialContext();
            String jndiName = "osgi:service/" +
                CurrentOffers.class.getName();
            CurrentOffers offers =
                (CurrentOffers) ctx.lookup(jndiName);
            html.append("<table>");
            List<SpecialOffer> currentOffers =
                offers.getCurrentOffers();

            for (SpecialOffer offer : currentOffers) {
                writeRowForOffer(html, offer);
            }
            html.append("</table>");
        } catch (NamingException e) {
            html.append("We have no special offers today. " +
                        "Try again tomorrow.");
        }
        html.append("</html>");
    }

    private void writeRowForOffer(PrintWriter html,
                                 SpecialOffer offer) {
        html.append("<tr>");
        String description = offer.getDescription();
        Food offerFood = offer.getOfferFood();
        html.append("<td>" + offerFood.getName() + "</td>");
        html.append("<td>" + offerFood.getPrice() + "</td>");
        html.append("<td>" + description + "</td>");
        html.append("</tr>");
    }
}

The osgi:service/ namespace provides access to services in the OSGi Service Registry. Services are registered under their interface name, which makes them easy to find. If no services implementing the SpecialOffer interface are registered, a NamingException will be thrown. In this case that’s not a problem—special offers aren’t compulsory! (The sharp-eyed among you will notice that we’ve abandoned internationalization again to keep the sample shorter.)

Implementing SayHelloJNDI has added some new dependencies, and that needs to be reflected in the manifest. The fancyfood.offers, fancyfoods.food, and javax .naming packages need to be imported:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-SymbolicName: fancyfoods.web
Bundle-Version: 1.0.0
Bundle-ClassPath: WEB-INF/classes
Web-ContextPath: /fancyfoods.web
Import-Package: fancyfoods.offers;version="[1.0, 2.0)",

fancyfoods.food;version="[1.0, 2.0)", javax.naming,
javax.servlet;version="[2.5, 3.0)",
javax.servlet.http;version="[2.5, 3.0)"

Finally, you need to add a new entry into your web.xml to register our servlet:

<servlet>
    <servlet-name>SayHelloJNDI</servlet-name>
    <servlet-class>fancyfoods.web.SayHelloJNDI</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>SayHelloJNDI</servlet-name>
    <url-pattern>/SayHelloJNDI</url-pattern>
</servlet-mapping>

Try adding the updated fancyfoods.web to the load directory before the fancyfoods.api bundle. There’s no need to restart the OSGi container; file changes will be detected and the bundle will be automatically refreshed. When you list the bundles with ss, fancyfoods.web will only be INSTALLED instead of STARTED. This means some of its dependencies were missing. When you copy across the fancyfoods.api JAR into the load directory, the fancyfoods.web bundle will spontaneously leap into life and become STARTED.

When you load the SayHelloJNDI web page, you should see a helpful message explaining that there are no special offers (figure 2.16).

Figure 2.16. Before the aggregator bundle is started, the Fancy Foods welcome page won’t be able to display any special offers.

After dropping your aggregator into the load directory, a CurrentOffers object will be available in JNDI, but there won’t be any special offers available, so the web page will show an empty table. Adding the chocolate bundle to the load directory will cause the chocolate offer to be injected into the offer aggregator (see figure 2.17).

Figure 2.17. After the aggregator and chocolate bundle are loaded, the page will display a special offer for chocolate.

2.5. Summary

If you’ve been working through the Fancy Foods application with us, you’ve achieved a lot in this chapter. Take a moment to pat yourself on the back. You now have a simple enterprise OSGi container going. You’ve written a web application, hooked components together with dependency injection, and integrated Blueprint with legacy Java using JNDI. You have half the building blocks you’ll need for a complete enterprise application. We’ll cover the missing blocks in the next chapter—persistence and transactions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.255.87