Chapter 9. Improve the Bookshelf Service with iPOJO

So far, you've noticed while implementing our case study that we've used the bundle activator to register services with the framework. We've also looked at them from the framework's perspective when we needed to use them.

However, we've had to look up the service every time it was needed, to ensure that we always have the latest valid instance that is registered with the framework.

A developer may add a listener to framework activity (a service tracker) and update references on changes to dependencies of interest. Not only is this a more complex functionality to implement, but the resulting code is also mostly boiler-plate, that is, it is very similar from bundle to bundle one usually ends up copying and pasting pieces of code or writing a common library to manage it. This is where iPOJO comes in handy.

In this chapter, we will look at how iPOJO can help keep this process simple, while improving the overall performance. You will:

  • Look at an overview of the inversion of control component-oriented programming patterns and its application in our context, namely, the service locator, dependency injection, whiteboard, and extender patterns
  • Introduce iPOJO, the service and Maven plugin, and look at the ways it is used to simplify OSGi integration, using XML configuration as well as annotations
  • Learn how to use the iPOJO Gogo commands to get a view of registered iPOJO instances and factories

You will also:

  • Simplify the case study bundles by migrating them to iPOJO, keeping their functionality unchanged.

What is Inversion of Control?

Inversion of control (IoC) is a group of design patterns (part of the Component Oriented Programming paradigm) in which logic, that was otherwise controlled by one component, is provided to it by another one. This logic can be related to the communication with a service, the instantiation of dependencies, their configuration, and so on.

In a classical procedural program, the main code block defines a sequence of steps that constitutes the program's execution flow. The program starts, initializes some variables, sets up connections to external systems, and executes its logic. It needs to:

  • Know where to get its configuration, how to read it, and initialize the properties of its components
  • Know which systems it needs to connect to, where they are, the connection adapters, and so on
  • Know how to perform its business logic

However, from the preceding activities, only the last one is really a necessary responsibility of that piece of code. For example, the configuration could be stored in a file, in a database, or provided by another service— all that this component cares about is it being configured. Similarly, with the connection to external systems, the component should not care which adaptor is providing the connectivity, as long as it follows a defined interface.

The main idea behind IoC is to relinquish the fulfillment of those tasks to other components. The result is that the main component is only responsible for its area of concern, with other components taking care of tasks like providing configuration objects, initializing links to services, updating the service references when they are no longer available, and so on.

The progressive move from monolithically stand-alone applications to framework-based component-oriented designs makes the implementation of this inversion of control more reachable. The framework can provide the functionality needed to facilitate these kinds of patterns and add-on components implement them.

A good example that we've already covered is the bundle activator taking control of the execution flow at the point where a bundle is starting or stopping. In this scenario, the framework performs all the tasks that are common to all bundles loading, resolving, and so on. The framework then inverts the control by handing it to the bundle activator while the bundle is starting (or stopping). The bundle knows what needs to be done during that part of the process, but doesn't need to know more.

In this section, we'll look at four inversions of control patterns that are of interest in the context of iPOJO. The first two relate to the initialization of references to external services, the other two relate to the tracking and reaction to the registration of other bundles like:

  • The Service Locator pattern
  • The Dependency Injection pattern
  • The Whiteboard pattern
  • The Extender pattern

The Service Locator pattern

We've already seen one inversion of control pattern earlier in this book without really naming it— the Service Locator pattern, by which a service (for example, the bookshelf service) does not instantiate a bookshelf inventory implementation itself. Instead, it requests one from the framework. The framework is responsible for providing the service implementation for the interface.

The Service Locator pattern

In this case, the bookshelf service does not know where the bookshelf inventory implementation comes from and does not need to worry about instantiating it. That control is inverted and delegated to the framework.

The Dependency Injection pattern

Another inversion of control pattern is dependency injection. In dependency injection (DI), the dependencies of a component are set and managed by an external component.

The external component not only knows how to find the required dependencies, but also injects them into the component that requires them.

The injection mechanism varies, depending on the container and the designer's preferences, but generally it's one of the following:

  • The dependency is passed as a parameter at the construction of the consumer
  • The dependency is passed as a parameter to a setter exposed by the consumer
  • The dependency is assigned to a property declared by the consumer

We will be looking at that last option in this chapter.

In our example, by using iPOJO to provide dependency injection, we delegate keeping track of the available services, registering them when they are started, and also assigning them to declare service instance attributes.

We'll have the following setup:

The Dependency Injection pattern

Since we were using the bundle activator of the bookshelf inventory implementation bundle solely for registering the service, we will no longer need it.

The choice of whether to use the Service Locator or Dependency Injection pattern really depends on the context. In here, we clearly see the benefit of using dependency injection, as it greatly simplifies the task of integrating with the framework.

Tip

An interesting article to read on inversion of control and dependency injection, by Martin Fowler, can be found at http://martinfowler.com/articles/injection.html

The Whiteboard pattern

Also used frequently in the context of OSGi, the whiteboard pattern is basically the matching of service providers to service consumers:

  • A service consumer, or an agent working on its behalf, registers with the framework to listen for a specific target (the consumed service)
  • The framework notifies the listener with added and removed instances of the consumed service

The Whiteboard pattern is used as a base for other patterns. For example, a dependency injection mechanism would most probably use the whiteboard pattern to keep track of registered services.

We will also use the whiteboard pattern to register our servlets, instead of registering them directly with the Http Service in Chapter 11, How about a Graphical Interface?. In that context, the HTTP whiteboard implementation will listen to registered HttpServlets and will add them to the installed Http Service, thus implementing the Extender Pattern.

The Extender Pattern

Based on the Whiteboard pattern, in the Extender Pattern will react to notifications of bundles starting or stopping and identify those that are of interest to it. This identification is usually through a manifest header or a configuration file in the bundle package. The extender component then registers them with a service provider as extensions.

We will see one example of the extender pattern in Chapter 13, Improving the Graphics, when looking at Pax Web. In this scenario, bundles are tagged with a specific manifest header that identifies them as web-applications and provides additional resources (such as a web.xml file).

The web extender component listens to bundles starting and stopping, picks those that have the required tag, and registers them with the Http Service.

The following diagram depicts this relationship:

The Extender Pattern

In the preceding example, Bundle A does not carry the required tag and therefore is not included in the extension mechanism.

Bundles B and C are not aware of the steps that are required to register themselves with the Http Service. They will be resolved by the framework. Then, as they are starting, the extender receives a notification, identifies them as extensions, and publishes them with the provider.

Later, when they are stopping, the extender also takes care of un-publishing them and cleaning up the resources that they provided.

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

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