Wednesday, July 18, 2018

OSGi Community Event 2018 Early Bird Pick & Registration Now Open

Congratulations to Lisa Nafeie from DLR, the German Aerospace Centre.  Lisa's talk, Visualization of OSGi based Software Architectures in Virtual Reality, has been selected as the Early Bird pick for the OSGi Community Event 2018.

We were lucky to have the opportunity to speak with Lisa to get some further background on her talk.  You can find our questions and her answers on the OSGi website.

We hope that this talk gives you a taste for all of the interesting OSGi content we will have at this year's Community Event in Ludwigsburg in October.  The OSGi Program Committee are busy reviewing all of the submissions to put a packed program together again this year.

If you are already planning on joining us you will be pleased to know that Registration for the event is now open and you can secure the best prices by booking early.

Monday, July 2, 2018

OSGi Presentation Recordings with BGJUG and Software AG

For those who follow this blog, you will know that we had an Evening of OSGi in mid-April hosted by BGJUG and Software AG. I am pleased to be able to share with you the video recordings that Software AG made at the meetup.  These have been posted on the OSGi Alliance YouTube channel and are available for you to watch.


There were two talks presented by the OSGi Alliance:
  • An 'Introduction to OSGi' by BJ Hargrave, OSGi Alliance CTO
  • A look at 'How OSGi Alliance specifications have influenced the IoT market' by Pavlin Dobrev & Kai Hackbarth (Bosch)
In addition, Todor Boev from Software AG presented 'OSGi and JPMS in practice' which discussed Software AG's experiences with OSGi and Jigsaw/JPMS.

Thanks once again to BGJUG and Software AG for arranging the meetup and allowing the Expert Group members to meet with the local Java community.

Tuesday, June 19, 2018

OSGi R7 Highlights: Configuration Admin and Configurator

The OSGi Compendium Release 7 specification contains an update to the Configuration Admin Service specification which includes a number of new features and also introduces the new Configurator specification.

Configuration Admin


One of the most used but on the other hand barely noticed services from the OSGi compendium specification is the Configuration Admin. This service allows to create, update and delete configurations. It is up to the implementation where these configurations are stored. A configuration has a unique persistent identifier (PID) and a dictionary of properties.

Usually, configurations are tied to an implementation of an OSGi service but configurations can be used for any purpose like database connections, current temperature or the set of available nodes in a cloud setup. While the Configuration Admin service has an API to find configurations or create them, it supports a more inversion of control like behavior by supporting a callback mechanism. The callback (known as the ManagedService interface) gets invoked for existing/created configurations of a certain PID and also if that configuration is deleted.

While this callback exists, again it's not that common to use it directly. The most common and easiest way to develop OSGi components and services is to use Declarative Services. Declarative Services (DS) provides build-in support for configurations. Simply by implementing an activation method the component can get its configuration. The implementor of that component does not have to worry whether such a configuration exists, gets deleted or is modified. DS takes care of all of this and invokes the right actions on the component.

In addition to single configurations, Configuration Admin provides support for factory configurations: multiple configurations of the same type, like for example different logger configurations for different categories or different database connection configurations. These factory configurations have a factory PID which is the same for all configurations of that type and a configuration PID to distinguish those configurations.

This should explain the big picture. Let's start talking about the new things in Release 7.

Configurator Specification


The Configurator specification is a new specification in the R7 release. It basically consists of two parts. The first part describes a common textual representation of a set of configurations. Previous to this specification each and every tool was using its own format for provisioning configurations. For example, the famous Apache Felix File Install uses a properties like format. Other tools use slightly different formats. One problem is that you can't simply switch from one tool to another and the other major problem is that some of the formats do not allow to specify the real type of a property value. For example, the value for the service ranking property must be of type Integer. Or you might have a special implementation that is expecting (for whatever reason) a value to be of type Byte. However, some tools are simply always using a Long to represent numbers or a String to represent anything else.

Therefore a common definition eliminates these problems and allows interchangeability of configurations between various tools. The format is JSON based and uses the PIDs of a configuration as the keys. The value is the configuration object with the properties:

{
    "my.component.pid": {
        "port:Integer" : 300, 
        "array:int[]" : [2, 3, 4], 
        "collection:Collection<Integer>" : [2,3,4], 
        "complex": { 
            "a" : 1, 
            "b" : "two"
        }
    }
}

As you can see in the example, it is possible to specify the runtime type of a configuration property by separating the property name from the type using a colon. For example, the "port" property value is of type Integer, the "array" property value is an array of ints and the "collection" property value is of type Collection<Integer>. You can specify all allowed types for a configuration and the configurator implementation uses converting rules as defined by the Converter specification - another one of the new specifications of Release 7.

In addition, a configuration property can hold structured JSON as a string value. In the example above "complex" contains at runtime a string value of the specified JSON.

Factory configurations can be specified by using the following syntax: FACTORY_PID~NAME. With the updated Configuration Admin it is possible to use a meaningful name to address factory components. The tilde separates the factory PID from the name:

{
    "my.factory.component~foo" : {
        ...
    },
    "my.factory.component~bar" : {
        ...
    }
}

Please note the errata for the published specification.

OSGi Bundle Configurations


The second part of the configurator specification describes a new extender based mechanism that picks up configurations from within a bundle and applies them. A bundle can contain one or more JSON files with configurations and once the bundle is started the configurations will be put into Configuration Admin by the Configurator. The configurator manages the state handling and ordering in a deterministic way. For example, if two bundles contain a configuration for the same PID, a ranking mechanism is used to specify which configuration is put into Configuration Admin, regardless of their installation or start order.

In addition to provide configurations through bundles, the Configurator supports providing initial configurations through system properties on startup of the OSGi framework. This is especially useful for customising an application without changing the distributable for the application. By specifying the system property configurator.initial with either a JSON document as described above or a list of URLs pointing to such JSON documents, the Configurator will apply the contained configurations in the same manner as if they would have been provided through a bundle.

With this new feature, provisioning of configurations through bundles and allowing to override them on startup becomes part of the OSGi specifications. You will find an example application using the Configurator at the OSGi enRoute website. The specification of the Configurator has driven the update of the Configuration Admin specification. So let's talk about the most important new features in Configuration Admin.

Improved Factory Configuration Handling


The handling of factory configurations has been greatly improved. With previous versions, when you create a factory configuration, the PID part is randomly generated which makes identifying a particular factory configuration later on much harder. In addition, as the PID is auto-generated, it has no meaning. With the updated Configuration Admin, it is now possible to specify the PID of a factory configuration, eliminating those problems.

New methods on the Configuration Admin allow to create and retrieve factory configurations based on the factory PID and a name. These methods behave the same as the already existing methods for plain configurations. The PID for those factory components is generated by appending the name to the factory PID separated by a tilde. The Configurator uses this syntax to specify factory configurations as shown above.

Improved Configuration Plugin Handling


When a configuration is delivered to a managed service, the configuration is passed through registered configuration plugin services. Such a service can manipulate the configuration. One common use case is to handle placeholders in the configuration properties and replace them with real values when delivered. For example, a property of a database connection configuration could just contain the value "${database.url}" which is replaced with the actual URL when this configuration is passed to the component processing the configuration. Or if you have sensitive configuration data, you can store it encrypted in the configuration and just decrypt it in a configuration plugin before it is passed to the managed service.

While this mechanism sounds useful, it is only useful if you register a managed service. However, when you are using Declarative Services (or other component frameworks) for your components, the plugins are not called at all. This gap is closed now and the DS implementation uses a new functionality of the Configuration Admin service and calls the plugins before it is passing the configuration to its components. This ensures plugins will be called regardless how you get your configuration. And this is making those use cases mentioned above possible.

Conclusion


The standard format for OSGi configurations is a great step forward for tooling and the Configurator implementation allows to deploy configurations through bundles in a standardized and well-specified way. The update of the Configuration Admin resolves some long outstanding issues and allows for new use cases around configuration management. For all the new features of the Configuration Admin service, have a look at the specification and make sure to also read the new Configuration specification.


Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:
  1. Proposed Final Draft Now Available
  2. Java 9 Support
  3. Declarative Services
  4. The JAX-RS Whiteboard
  5. The Converter
  6. Cluster Information
  7. Transaction Control
  8. The Http Whiteboard Service
  9. Push Streams and Promises 1.1
Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when it's available.

Tuesday, June 5, 2018

OSGi R7 Highlights: Push Streams and Promises 1.1

The OSGi Compendium Release 7 specification contains new and updated specifications from the OSGi Alliance. Today I have the good fortune to be writing about one of each! The OSGi Push Streams specification is a brand new reactive data processing API in R7, and is closely related to the newly updated OSGi Promises 1.1 specification.

OSGi Utility Specifications

Before we dive into the new features provided by both of these specifications I will highlight an important, and often overlooked, fact about the OSGi specifications. You may have noticed that there are some large gaps in the OSGi specification numberings, in particular, that there is a really big gap between chapters 151 and 702.

This numbering isn't an accident. Chapters numbered in the range 700-799 are "utility" packages. These differ from other OSGi compendium specifications in that they don't define OSGi service interfaces that you look up from the service registry, instead they define classes that you use directly. This means that the OSGi utility specification packages are self-implementing, i.e., when you get the specification jar you get the reference implementation at the same time!

Push Streams and Promises are both utility specifications because the types and behaviors they define don't fit a service model, instead, they provide abstractions for asynchronous behaviors. There is, however another important feature of both specifications which makes them particularly special. Neither specification has any dependency on the OSGi Core specification! This means that not only does downloading the specification API give you an implementation, but you can also use that specification outside of an OSGi framework. Push Streams and Promises are therefore great technologies to use in any Java application that you write.

What are Push Streams?

The OSGi Push Streams specification is completely new for OSGi R7, and it defines a model for processing asynchronous streams of data using Reactive Programming techniques. Reactive Programming simply refers to a program which responds to the arrival of data or events, rather than attempting to pull data or events from the source. Reactive systems are also expected to run asynchronously and to process long running (or even infinite) streams of data.

Streaming data with Java Streams

You're probably familiar with the Java Stream API which was added in Java 8. The Stream API provides a rich functional mechanism to process streams of data (typically Java Collections).
List<Person> guests = getWeddingGuestList();

// How much wine will we need to buy?
long adults = guests.stream()
    .map(Person::getAge)
    .filter(age -> age > 21)
    .count();

Processing data using the Stream API allows you to write simple, effective code, however, it does not work well for data that cannot be generated on demand. If the list of wedding guests in the above example were based on email responses then the processing thread would spend a huge amount of time blocked polling the email server!

Streaming event-based data using Push Streams

Event-based data is data that occurs when a specific action happens. This may be a clock tick, a user clicking on a web-page, or it may indicate a train passing a signal. The important thing about event-based data is that it is generated based on an external stimulus, and not because a consumer asked for the data.

Push Streams differ from the Java Stream API because they expect data to be generated and processed asynchronously. The API, however, remains very similar:
PushStream<Person> guests = getWeddingGuestList();

// How much wine will we need to buy?
Promise<Long> adults = guests
    .map(Person::getAge)
    .filter(age -> age > 21)
    .count();

The most important difference is that the return value of a Push Stream's terminal operation is a Promise. This allows the Stream of asynchronously arriving data to be processed in a non-blocking way.

Creating your own Push Streams

Push Streams can be created easily using a Push Stream Provider. A Push Stream Provider can be configured to use specific thread pools, queueing policies, back-pressure, and circuit breaker behaviors.
PushEventSource<Email> emails = getWeddingEmailResponses();

PushStreamProvider psp = new PushStreamProvider();

// How much wine will we need to buy?
long adults = psp.createStream(emails)
    .map(Email::getFromAddress)
    .map(this::lookupPersonByEmail)
    .map(Person::getAge)
    .filter(age -> age > 21)
    .count();

Once you have a Push Stream Provider you can use (and re-use) it to create a Push Stream from any Push Event Source. While it is possible (and sometimes desirable) to create your own Push Event Source implementation, in most cases, you can create a Simple Push Event Source from the Push Stream Provider. Events can then be pushed into the  Simple Push Event Source as they occur, and they will be passed into any connected streams.
PushStreamProvider psp = new PushStreamProvider();

SimplePushEventSource<Email> spes = 
        psp.createSimpleEventSource(Email.class);

// When an "email" event occurs
spes.publish(email);

// When an error occurs
spes.error(anException);

// If/When the data stream finishes
spes.endOfStream();

Buffering and Back Pressure

In the Java Stream API the rate of data processing is determined by how fast the consumer can pull data from the source. This places a natural "brake" on the system, where the consumer cannot be overwhelmed by incoming data. In an event-based system this is no longer the case! Data events may occur far more rapidly than they can be consumed.

You can always attempt to consume events using more threads, but sooner or later your system will run out of capacity, and events will have to be queued until they can be processed. A queue of events is usually called a Buffer, and buffering is natively supported by the Push Stream specification

Buffers are useful for dealing with short-term spikes in the flow of events, but they cannot help if the long-term event arrival rate is higher than the long-term consumption rate. In this case the only options are to:
  • Discard some of the events
  • Fail the stream
  • Communicate to the event source that it should slow down
Deciding whether to discard events or fail the stream is the job of the buffer's Queue Policy. There are several built-in Queue Policy Options which provide basic behaviors, but you can implement your own behaviors if desired. Telling the producer to slow down, however, is the job of back pressure.

Back pressure in a Push Stream is a long indicating the number of milliseconds for which the event source should stop sending events in order to give the consumer time to catch up. Back pressure can be provided in a number of ways.
This back pressure is then sent back to the event producer, which may (or may not) slow down as a result.

Using Push Streams with Real Data

The UK's rail network produces data events every time a train passes a monitoring point. This information is used to help manage signalling, report delays, and for all sorts of other reasons. This data is also part of the UK government's open data program, and has a feed streaming data to anyone who signs up for an account!

There is a public example on GitHub using Push Streams to consume this data. The data can be consumed live, however, to avoid users having to sign up for an account this example replays events recorded from the real stream.

The incoming events are JSON arrays containing batches of data events. The main stream pipeline can be seen here. It uses Jackson to read the JSON, filter the data for interesting event types, map the data into Java types and then turns those data events into train reporting locations.

What's New in Promises 1.1?

The 1.1 update to Promises addresses a number of different areas.

Time-based behaviors

The first version of the OSGi Promise API gave you no way to deal with the passage of time. For example what should happen if a Promise isn't resolved for a very long time, or if it is resolved much faster than we expect?

The timeout and delay methods have been added to the Promise API to allow more sophisticated time-based behaviors to be built using Promises.

  • The timeout method returns a Promise which fails with a TimeoutException if the original Promise doesn't resolve before the supplied timeout elapses.
  • The delay method returns a Promise which does not resolve until the supplied delay has elapsed after the original promise resolves.
Using a combination of timeouts and delays can throttle a busy system, or ensure a live feel in a system that is receiving little input.

Thread management

Probably the largest change in the Promises specification is the introduction of the PromiseFactory. The Promise Factory is a powerful type which can be used as a factory for Deferred instances and resolved Promises. In addition, the Promise Factory can also be used to define the threads that should be used to execute callbacks, and to manage the time-based behaviors of the Promises. By changing the properties of the thread pool used by the Promise Factory you can ensure your callbacks are executed serially, or in parallel, or on the same thread that initiated the callback.

Usability improvements

Some of the less obvious improvements to Promises are the small usability enhancements that have been made to the API.
  • The OSGi callback functions now declare throws Exception so that your promise usage is more lambda friendly!
Promise<String> pString = getStringPromise();

// No need to worry about the URISyntaxException
Promise<URI> pURI = p.map(URI::new);
Promise<String> pString = getStringPromise();

// Doing work onResolve used to be a bit messy
pString.onResolve(() -> {
    Throwable t = pString.getFailure();
    if(t != null) {
      try { finished(pString.getValue()); } catch (Exception e) {}
    } else {
      logFailure(t);
    }
  });

// It is much simpler now!
pString.onSuccess(this::finished)
       .onFailure(this::logFailure);
  • A new thenAccept method for when you don't need the full power of then 
Promise<String> pString = getStringPromise();

// This behavior
pString.then(p -> {
    storeValue(p.getValue());
    return null;
  });

// becomes
pString.thenAccept(this::storeValue);

In Summary

OSGi Push Streams and Promises are powerful tools for asynchronous programming, even if you're not an OSGi user! If you're interested in using Push Streams or Promises then the implementations are freely available for you to start using now.



Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:
  1. Proposed Final Draft Now Available
  2. Java 9 Support
  3. Declarative Services
  4. The JAX-RS Whiteboard
  5. The Converter
  6. Cluster Information
  7. Transaction Control
  8. The Http Whiteboard Service
  9. Configuration Admin and Configurator
Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when it's available.

Tuesday, May 22, 2018

OSGi R7 Highlights: The Http Whiteboard Service

The OSGi Compendium Release 7 specification contains version 1.1 of the Http Whiteboard specification which includes a number of new features.

Before we dive into the new features, let's start with a summary of what the Http Whiteboard Specification is about: It provides a light and convenient way of using servlets, servlet filters, listeners and web resources in an OSGi environment through the use of the Whiteboard Pattern. The specification supports registration of the above-mentioned web entities and grouping them together in context. A three-part introduction into the Release 6 version of the Http Whiteboard can be found here, here and here.

Component Property Types


Registering web entities using the Http Whiteboard usually requires specifying several service registration properties. In Release 7, Declarative Services added the ability to use component property types to annotate components and set property values in a type-safe manner. A set of annotations has been added to the Http Whiteboard specification to make use of this new feature in Declarative Services, simplifying the development of such web entities.

For example, registering a servlet at the path /game looks now like this:
@Component(service = Servlet.class)
@HttpWhiteboardServletPattern("/game")
public class MyServlet extends HttpServlet {
  ...
}

Similarly, defining additional service properties can easily be done by adding more annotations to the class. In the following example, we declare the servlet to support asynchronous processing and mount the servlet in a specific Http Context (in contrast to using the default context as above):
@Component(service = Servlet.class)
@HttpWhiteboardServletPattern("/game")
@HttpWhiteboardContextSelect("(" 
  + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME
  + "=mycontext)")
@HttpWhiteboardServletAsyncSupported
public class MyServlet extends HttpServlet {
  ...
}

Further annotations have been added to simplify the development of servlet filters, resources, listeners error pages, and servlet context. A full list of these can be found here.

Multipart File Upload


Support for multipart file upload handling and configuring this handling has been added. The possibilities are the same as supported by the servlet specification. The multipart handling can be enabled for a servlet by specifying additional service registration properties. Again, using a component property types simplifies the specification of the required properties. In the following example we enable multipart file upload for the servlet and restrict the size of the uploaded files to 500,000 bytes:
@Component(service = Servlet.class)
@HttpWhiteboardServletPattern("/game")
@HttpWhiteboardServletMultipart(maxFileSize=500000)
public class UploadServlet extends HttpServlet {
  ...
}

The chapter about Multipart File Upload contains a complete description of the service properties for multipart file upload.

Pre-Filtering


A servlet filter is registered with an Http Context together with some rules when the filter is applied, e.g., by specifying a path pattern or a servlet name. However, servlet filters are run after a potential user authentication and therefore never get run when this authentication fails. In addition, these filters are only run if the request is targeting an existing endpoint, either a servlet or a resource.

On the other hand, some use cases require running some code with every request or before authentication. For example, logging all requests, regardless of whether authentication is successful is one of those use cases. Preparing the request by adding additional information from a third party system might be another one.

With the updated Http Whiteboard specification, a new web entity, the Preprocessor has been added. All services registered with this interface are invoked before request dispatching or authentication is performed. Therefore such a preprocessor will receive all requests. The Preprocessor interface is just an extension of the servlet filter interface and just acts as a marker to distinguish a preprocessor from a normal servlet filter. The following example implements a simple preprocessor, logging all requests:
@Component(service = Preprocessor.class)
public class LoggingFilter implements Preprocessor {

  public void doFilter(ServletRequest request,
                       ServletResponse response,
                       FilterChain chain)
  throws IOException, ServletException {
    System.out.println("New request to "
      + ((HttpServletRequest)request).getRequestURI());
    chain.doFilter(request, response);
  }

  public void init(FilterConfig filterConfig)
  throws ServletException {
    // initialize the preprocessor
  }

  public void destroy() {
    // clean up
  }
}

As a preprocessor is invoked for every request, there are no special service properties for this type of service, especially this service is not associated with any Http Context as the dispatching to a context happens after the preprocessors are invoked.

Updated Security Handling


Security handling or authentication can be implemented by registering your own implementation of the ServletContextHelper service and implementing the handleSecurity method. Web entities like servlets or filters can then be associated with this context ensuring that they are only invoked if the authentication is successful.

While the handleSecurity methods provide a good mechanism to check for authentication and potentially add additional information to the current request like a user context which can then be used by the web components, a method for cleaning up such state was missing. With the update of the Http Whiteboard, a new method finishSecurity has been added which is now closing this gap. This new method is the counterpart of handleSecurity and is invoked when the request processing is done. By implementing this method any resources allocated through handleSecurity can be cleaned up.

More on the Http Whiteboard Update


This blog post mentions only those new features which I think are the most important ones of the new version 1.1. You can find the full list of changes at the end of the Http Whiteboard specification.


Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:
  1. Proposed Final Draft Now Available
  2. Java 9 Support
  3. Declarative Services
  4. The JAX-RS Whiteboard
  5. The Converter
  6. Cluster Information
  7. Transaction Control
Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when it's available.

Thursday, May 17, 2018

OSGi Community Event 2018 - CFP Now Open

We are pleased to announce the OSGi Community Event 2018 and that the Call For Papers (CFP) for this year's conference is open.

The OSGi Community Event 2018 is returning to Ludwigsburg in Germany where we are pleased to be co-located with EclipseCon Europe again.  The event will take place October 23-25 inclusive and will feature an OSGi tutorial, talks, a BOF and other OSGi community-related activities.

Attendees will have access to the full program at the OSGi Community Event and EclipseCon Europe.


CALL FOR PAPERS IS OPEN

We are looking for one, 3-hour tutorial and many more 35-minute standard talks. The Call for Papers (CFP) is open to anyone who has an experience, expertise or story to share about OSGi technology or the OSGi ecosystem. We are particularly interested in use cases and new initiatives around OSGi in enterprise, embedded, Cloud, Telco, and IoT.

The Call For Papers for this year's event is open now and closes 16 July.

If you fancy a chance to win a €50 Amazon voucher, then get your submission in by 2 July and you will be considered for the Early Bird pick where the winner will receive the voucher.

You can find out further details and information on how to submit a talk or tutorial on our conference website.

There you can also find links to speaker and submission FAQs that EclipseCon has put together.

If you have any questions about submissions or the conference in general, then please contact us.

We are looking forward to reading your talk and tutorial submissions and also seeing the OSGi Community in Ludwigsburg in October.

Tuesday, May 8, 2018

OSGi R7 Highlights: Transaction Control

The OSGi R7 specification contains a wide variety of exciting new features covering a wide variety of different use cases. The OSGi Transaction Control service is one of these new specifications providing modularity for transactional resource management.

A New Take on an Old Problem


Transactions have been used in software for decades, and over time they have become simpler to use. The Java Transaction API provides a common API for transaction management in Java, but this is still considered too hard to use directly. Java EE and the Spring framework, therefore, created a variety of declarative models. Before we talk about the new OSGi Transaction Control service we should understand how to work (or not) with these technologies.

Working with Declarative Transactions


The recommended approach for using transactions in Java EE and Spring is to apply the @Transactional annotation to your transactional methods.
public class TransactionalBean {

  DataSource ds;

  @Transactional
  public void addUser(String user) throws SQLException {
    // Add the user
    try (Connection conn = ds.getConnection();
         Statement s = conn.createStatement(
                 "insert into users values(?)")) {
      s.setString(1, user);
      s.executeUpdate();
    }
  }
}

This solution is incredibly simple, unfortunately, it is also deceptively so. Rather than the explicit complexity of managing your own transactions all of the complexity is hidden behind the @Transactional annotation. Unfortunately, hiding complexity doesn't make it go away, it just leads to other questions.
  • How did the transaction actually start and stop?
  • How did the database connection know to participate in the transaction?
  • What will cause the transaction to roll back?

Answering the Questions 


It turns out that there are a lot of moving parts behind the curtain!

Firstly, the transaction is started and stopped by container code running immediately before and after your method runs. This is almost always achieved by creating a proxy for a managed instance and ensuring that all access to the instance is through the proxy. Unfortunately, it's pretty easy to violate that restriction:
public class TransactionalBean {

  ...

  @Transactional(SUPPORTED)
  public void addIfNecessary(String user) {
    if(!userExists(user) {
      addUser(user);
    }
  }
}

In this case, we have another method on our object which can run without a transaction. When this method makes a call to the addUser method it does not touch the proxy. As a result, we can end up running the addUser method outside a transaction.

Secondly, the data source is enlisted because it is also proxied - when the getConnection call is made it locates the current transaction and enlists the connection with that transaction. Importantly this only works if the same person is doing all the proxying and uses the correct transaction manager.

Thirdly, a Java EE (or Spring) transaction will roll back if the method completes with an unexpected exception. Checked exceptions are part of the method signature and therefore not considered to be unexpected. This means that the SQLException in our example does not trigger a rollback

The biggest problem with proxying is that you need to have an all-knowing container ready, running and responsible for managing the objects and resources in the system. In Java EE this is the Application Server, in Spring it is the Application Context, but in OSGi? One thing that we learn over and over is that for a system to be modular you cannot have a single global container. The provider of the resources and the provider of the business objects must be free to use whatever frameworks they choose. We also learn that for a system to be robust we cannot rely on other modules to start before we do. Proxying to provide transactions is, therefore, a fundamentally flawed approach in OSGi.

Transaction Control - A Modular Approach


The OSGi Transaction Control service is a new specification which is designed to address the issues with the Java EE/Spring transaction management model.

One of the biggest differences when using Transaction Control is that transaction management is programmatic, not declarative, and uses a functional decorator pattern. This means that there is no need for a proxy to introduce transaction management instructions into your code, and the transaction is guaranteed to start however your method gets called.
@Component
public class TransactionalComponent {

  @Reference
  TransactionControl txControl

  public void addUser(String user) throws SQLException {
    // Add the user
    txControl.required(() -> {
        // This scoped work runs in a transaction
        return 42;
    });
  }
}

The Transaction Control service offers convenient methods for:

  • Requiring a transaction
  • Requiring a new transaction
  • Suspending a transaction
  • Checking to see whether a transaction is active or not

Completing the Transaction


The transaction and all other state associated with the scope is completed when the scoped work returns. Normally the work will return a value, and this value will be returned by the required method (the integer 42 in the example above). Returning a normal value will cause the transaction to be committed. If the commit fails then the Transaction Control service will throw a TransactionException from the required method.

Rolling Back 


One other important difference between Java EE/Spring transactions and the Transaction Control Service is that in Transaction Control every Exception triggers rollback by default. This is much more likely to give the correct behavior when things go wrong.

The easiest way to get a rollback is, therefore, to throw an exception from your scoped work! If your scoped work does complete with an exception then this will be wrapped in a ScopedWorkException and re-thrown by the required method. Sometimes, however, you don't want to commit your work, but it isn't an exceptional circumstance. In this case throwing an exception is the wrong thing to do, instead, you should simply mark the transaction for rollback.

@Component
public class TransactionalComponent {

  @Reference
  TransactionControl txControl

  public void addUser(String user) throws SQLException {
    // Add the user
    txControl.required(() -> {
        // This transaction must roll back
        txControl.setRollbackOnly();
        return 42;
    });
  }
}


A full description of the scope lifecycle is available in the Transaction Control specification.

Scoped Resources


In order for a transaction to be useful, it must have one or more resources participating in it. In Java EE and Spring these resources must be managed by the container so that they can be enlisted. The Transaction Control specification changes this model to let the OSGi bundle take control of enlistment.

Scoped Resources are created from a ResourceProvider, an object which is either found in the service registry or created by a factory service. The ResourceProvider interface is generic and usually subclassed to provide a specific resource type. The Transaction Control specification includes standard interfaces for creating scoped JDBC Connection objects and JPA EntityManager instances.

The easiest way to get a resource provider instance is to configure the resource provider implementation. For example, the Reference Implementation from Apache Aries can be configured as follows (using the configurator JSON format)
{
    // Global Settings
    ":configurator:resource-version" : 1,
    ":configurator:symbolic-name" : "org.osgi.blog.tx.config",
    ":configurator:version" : "0.0.1.SNAPSHOT",
    
    
    // Configure a JDBC resource provider
    "org.apache.aries.tx.control.jdbc.xa~resource": {
           "osgi.jdbc.driver.class": "org.h2.Driver",
           "url": "jdbc:h2:./data/database" }
}

The configured JDBCConnectionProvider can then be combined with a Transaction Control Service to create a scoped resource and used as follows:

@Component
public class TransactionalComponent {

  private final TransactionControl txControl;

  private final Connection txConnection;

  @Activate
  public TransactionalComponent(
      @Reference TransactionControl txControl,
      @Reference JDBCConnectionProvider provider) {
    this.txControl = txControl;
    this.txConnection = provider.getResource(txControl);
  }

  public void addUser(String user) throws SQLException {
    // Add the user
    txControl.required(() -> {
      Statement s = txConnection.createStatement(
             "insert into users values(?)");
      s.setString(1, user);
      return s.executeUpdate();
    });
  }
}

Resource Lifecycle


You may have noticed that the example using Transaction Control never closes its connection or the Statement it creates. In fact, the same connection instance is used for every call to addUser! This isn't a mistake but in fact, the recommended way to make use of a scoped resource. When you call getResource on a resource provider you aren't being given a physical resource, but a transaction-aware resource object.

The first time that you call the scoped resource object inside a piece of scoped work it does several things:

  • It obtains a real physical resource from an underlying pool
  • It enlists the physical resource in the transaction (if there is one)
  • It registers the physical resource with the ongoing transaction context so the same physical resource is used for the rest of the scoped work
  • It registers a completion callback so that the physical resource can be automatically returned to the pool
All of this means that you never need to worry about getting a different connection instance, or about closing it when you're done. All resource access is implicitly and automatically bounded by the scoped work, this includes the objects created by the scoped work such as Statements and ResultSets.

In Summary


The Transaction Control Service provides a simple, reliable, modular solution for transaction lifecycle management and resource access. If you're interested in seeing more usage of Transaction Control then you should check out the data access services from the OSGi enRoute for R7 microservice example.



Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:
  1. Proposed Final Draft Now Available
  2. Java 9 Support
  3. Declarative Services
  4. The JAX-RS Whiteboard
  5. The Converter
  6. Cluster Information
Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when it's available.

Tuesday, April 24, 2018

OSGi R7 Highlights: Cluster Information Specification

In my experience, one of the most powerful parts of the OSGi specification are the Remote Services and Remote Service Admin, which enable you to develop complex, distributed applications in a modular way. However, in order to deploy such a distributed application, it is key to find out on which devices bundles can be deployed. In previous releases, OSGi specifications for remote management are already available for certain domains (i.e. telecommunications with TR-069 Connector Service) or protocols (i.e., REST Management Service).

With OSGi R7, we now introduce the Cluster Information specification, a protocol-agnostic specification to discover, list and inspect the available devices in a distributed compute environment, and to provision these devices with OSGi bundles.

A Cluster of Nodes

At the heart of the cluster information specification, is the NodeStatus service, which is used to indicate the presence of a node within the cluster. This could be any entity, such as a virtual machine, a docker container, a physical machine, a database, your Raspberry Pi, etc. Each node has a globally unique identifier, a name of the cluster it belongs to, and optionally a number of other properties such as the endpoint at which the node can be accessed, the physical location where this node is located or some application specific tags. These properties are available as service properties on the Node Status service. For a full list of properties, take a look at the specification.

A special case of node is, of course, an OSGi framework, in which case we use the FrameworkNodeStatus as presence service, which extends NodeStatus. In addition to the Node Status service properties, this one also provides some OSGi-specific properties, such as the framework version, Java version, etc. Also, when an OSGi framework is part of a cluster, this means it gets access to remote services of any other OSGi framework in that cluster. Ensuring the discovery, visibility and access of remote services within the cluster is the responsibility of the Remote Service Admin.

As we all know service properties are quite cumbersome to parse and process. Therefore, we also provide DTO types NodeStatusDTO and FrameworkNodeStatusDTO to have type-safe access to these properties. Converting service properties to these DTO types can be easily done using the Converter specification. For example, the following component lists each node in the cluster with name mycluster and prints out its id.

@Component
public class MyClusterManagement {
    private static final Converter CONVERTER
                        = Converters.standardConverter();

    @Reference(cardinality=MULTIPLE, 
               policy=DYNAMIC,
               target="(osgi.clusterinfo.cluster=mycluster)")
    void addNode(NodeStatus ns, Map props) {
        // Convert properties to the DTO for type safe access
        NodeStatusDTO dto = CONVERTER.convert(props)
                                     .to(NodeStatusDTO.class);
        // Print out the node id
        System.out.println("Node added: "+dto.id);
    }
}

For more info on using the Converter, take a look at David Boschaert's blog post.

Provisioning Nodes

The Framework Node Status service also provides a way to interact with the OSGi framework by also extending the FrameworkManager interface. This provides a similar interface as the REST Management service, but accessible as an OSGi Remote service.

Now we can extend the previous example and write a provisioner that deploys some location specific bundles.

@Component
public class FrameworkProvisioner {
    private static final Converter CONVERTER
                        = Converters.standardConverter();

    @Reference(cardinality=MULTIPLE,
               policy=DYNAMIC)
    void addFramework(FrameworkNodeStatus fns, Map props) {
        // Convert properties to the DTO for type safe access
        NodeStatusDTO dto = CONVERTER.convert(props)
                                     .to(NodeStatusDTO.class);

        // Check the ISO 3166-1 alpha 3 country code
        if ("DEU".equals(dto.country)) {
            // If this framework runs in Germany
            // install a special bundle into it
            try {
                fns.installBundle("Germany specific bundle");
            } catch (Exception e) {
                // log
            }
        }
    }
}

Metrics

Besides announcing static properties about a node by means of the service properties, it is also possible for a node to give access to dynamically changing properties, which we call metrics. To that end, the NodeStatus service provides the getMetrics() method, which returns a map with the current metric values. Which metrics are actually provided depends on the type of node and is implementation specific. For example, a NodeStatus of a VM could return usage metrics of that VM given by your cloud provider.

Again, an implementation can also provide DTO types to convert this map to a type-safe object. For example, the Eclipse Concierge reference implementation provides a JMXMetricsDTO that exposes JMX metrics of the JVM.

// From service registry
NodeStatus ns = ...;
// Obtain all metrics for this node
Map metrics = ns.getMetrics();

// Convert the metrics map to a DTO for type-safe access
JMXMetricsDTO dto = Converters.standardConverter()
                              .convert(metrics)
                              .to(JMXMetricsDTO.class);

// Use metrics
System.out.println("System Load Average: " 
                   + dto.systemLoadAverage);

Case Study: OSGi Community Event Trains Demo

Those of you who attended the OSGi Community Event the last couple of years will most likely have seen the trains demo. This demo uses a number of different OSGi open source projects, and commercial products from OSGi Alliance members to manage and control the operation of LEGO® trains.
All the trains, signals, track switches, train control buttons, notification screens etc. are controlled by different Raspberry Pi devices, adding up to a total of 8 different devices running an OSGi framework that have to be provisioned with different bundles depending on their role in the demo. This makes it a very tedious setup to get all devices up and running correctly.

For this use case, we can use the osgi.clusterinfo.tags property of the Node Status service to associate application specific tags to each OSGi framework node, indicating the roles it has to fulfill (i.e., "switchcontroller","traincontroller","signalcontroller", ...). We can then use these tags in a provisioning component similar to the example above, installing the correct bundles depending on the roles.

These tags can be used in a very versatile way, and in fact, any bundle in the OSGi framework can add a custom tag to the Node Status service representing this framework. To do so, any bundle can register a service with the org.osgi.service.clusterinfo.tags property set, which will automatically be added to the Node Status service tags.

The Cluster Information specification can also be used to manage non-OSGi entities. For example, in the demo, we also used an MQTT broker to connect with various external sensors. Again, this resulted in configuring on each of the devices the correct URI for connecting to the broker. By using a Node Status service that announces the presence of the MQTT broker, we have a single point of configuration in the cluster, and any component can depend on the presence of the broker by adding the following reference.
@Reference(target="(osgi.clusterinfo.tags=mqttbroker)")
void setBroker(NodeStatus ns, Map props) {
   // Convert properties to the DTO for type safe access
   NodeStatusDTO dto = CONVERTER.convert(props)
                                .to(NodeStatusDTO.class);
   // Now connect to the broker given the endpoint
   connect(dto.endpoint);
}

Notice again how we can use a custom tag to indicate this is an MQTT broker.

Want to know more?

This blog post gave a quick overview on the Cluster Information specification and some potential use cases. For more detailed information, see the spec chapter. If you want to play around with some actual code, the reference implementation is available in the Eclipse Concierge repository, with an actual release to follow soon.


Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:
  1. Proposed Final Draft Now Available
  2. Java 9 Support
  3. Declarative Services
  4. The JAX-RS Whiteboard
  5. The Converter
Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when it's available.

Tuesday, April 10, 2018

OSGi R7 Highlights: The Converter

One of the entirely new specifications in the OSGi R7 set of specs is the Converter specification. The converter can convert between many different Java types and can also be a highly convenient tool to give untyped data a proper API.

Let's dive in with some simple converter examples:

// Obtain the converter
Converter c = Converters.standardConverter();

// Simple scalar conversions
int i = c.convert("123").to(int.class);
UUID id = c.convert("067e6162-3b6f-4ae2-a171-2470b63dff00")
           .to(UUID.class);
BigDecimal bd = c.convert(12345).to(BigDecimal.class);

Collections, arrays and maps can also be converted:

List<String> ls = Arrays.asList("978", "142", "-99");

// Convert a List of Strings to a Set of Integers, 
// note the use of TypeReference to capture the generics
Set<Integer> is = c.convert(ls)
                   .to(new TypeReference<Set<Integer>>(){});

// Convert between map-like structures
Dictionary<String, String> d = ...; // some dictionary
Map m = c.convert(d).to(Map.class);

// Even though properties contain String keys and values, you 
// can't assign them to a Map<String, String>, the converter 
// can make this easy...
Properties p = ...; // a properties object
Map<String, String> n = c.convert(p)
           .to(new TypeReference<Map<String,String>>(){});

The above examples show that many different types of conversions can be made with the converter, using the same convert(object).to(type) API.

Note that the converter is obtained by calling Converters.standardConverter() so by default it's not registered in the service registry. This makes the converter usable in both OSGi Framework as well as in plain Java environments.

Backing Objects

An interesting feature of the converter is that it can convert between maps and Java types such as interfaces, annotations and java beans. Together we call these map-like types. In certain cases the conversion can provide a live view over the backing object. 

For example, let's say you have an interface MyAPI:

interface MyAPI {
    long timeout();
    long timeout(long defval);
}

Now you have a map that contains a timeout key with some value, if you want to expose that via the MyAPI interface the converter can do this:

Map<String, Long> m = new HashMap<>();
m.put("timeout", 999L);
MyAPI o = c.convert(m).to(MyAPI.class);
System.out.println(o.timeout()); // prints 999
m.put("timeout", 1000L);         // update the underlying map
System.out.println(o.timeout()); // prints 1000

The other way around is also possible. For example, if you have a JavaBean that you need to view as a Map, then the converter can do this. In the following example, we see that in order for the converter to handle a source object as a JavaBean you need to specify the sourceAsBean() modifier. Additionally, live views for maps are not enabled by default and are enabled with the view() modifier.

MyBean bean = new MyBean();
bean.setLength(120);
bean.setWidth(75);
Map<String, String> bm = c.convert(bean).sourceAsBean().view()
           .to(new TypeReference<Map<String,String>>(){});
System.out.println("Bean map:" + bm);
bean.setWidth(125)                    // update the bean
System.out.println("Bean map:" + bm); // map reflects the update

In addition to the map-like types mentioned above, the converter can handle DTOs and when an object exposes a method called getProperties(), it can take this as the source map as well.

Defaults

Converting a map or properties object to a Java interface can be a really useful way to give some untyped configuration a well-defined API. Often you'd want to be able to specify some defaults for the configuration values that you're using, in case they are not specified. When converting to interfaces or annotations defaults can be specified.

When converting to an interface, the interface may define a one-argument method to specify the default, as is done with the MyAPI interface above. To specify the default pass it into the method:

Map<String, Long> m = new HashMap<>(); // empty map
MyAPI o = c.convert(m).to(MyAPI.class);

// default to 750 if no timeout is in the map
System.out.println(o.timeout(750L));

Handling defaults via an annotation is even a little neater, as the Java annotation syntax allows for the specification of defaults.     

@interface MyAnn {
    long timeout() default 100;
}

MyAnn a = c.convert(m2).to(MyAnn.class);
System.out.println(a.timeout()); // use default 100
m2.put("timeout", 500L);
System.out.println(a.timeout()); // now get the actual value 500


Customizing Converters

While the converter can convert between many types (see the spec chapter for the actual list of supported types) it doesn't understand every single Java type ever created. Let's say we have two custom types Foo and Bar. There is a conversion between the two, but the converter doesn't know about it. We can create a customized converter that, on top of its existing conversions, knows about our special conversion as well!

Let's say our custom classes look a bit like this. The Foo class can be represented as a string which you can obtain via its read() method. The Bar class can hold a byte array.

public class Foo {
    // ...
    public String read() {
        return val;
    }
}

public static class Bar {
    byte[] buf;

    public static Bar create(byte[] b) {
        Bar bar = new Bar();
        bar.buf = b;
        return bar;
    }
}

To create a converter that, on top of the existing conversions, knows about Foo and Bar, we use a converter builder and register a rule with that to do the conversion. In the example below, the Rule object declares the source and the target of the rule. The conversion is provided as a lambda:

Foo f = ...; // Some Foo object
Converter custom = c.newConverterBuilder()
    .rule(new Rule<Foo, Bar>(
        foo -> Bar.create(foo.read().getBytes())){})
    .build();

// Now we can convert Foo to Bar!
Bar b = custom.convert(f).to(Bar.class);

Now the converter can convert our custom objects. An additional benefit is that the converter is an immutable object. You can customize an already customized converter by creating a new converter based on the previously created custom one.

Because the converter is immutable it's ideal for sharing, so once you've customized the perfect converter for your application, a good way to share it is by registering it in the OSGi Service Registry, and because the default converter isn't available in the service registry, your application components can simply look up the Converter service to get the one you registered for application wide usage.

Getting Started

This blog post doesn't cover all the details of the converter specification. See the spec chapter for more info on those. Like to get started straight away? At the time of this writing the specs are still under vote and not yet finally released. However, you can obtain a snapshot converter implementation from the OSGi Maven Repo: https://oss.sonatype.org/content/repositories/osgi/org/osgi/org.osgi.util.converter/ or you can build one yourself from the codebase in the Apache Felix project: https://svn.apache.org/repos/asf/felix/trunk/converter/converter.



Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:
  1. Proposed Final Draft Now Available
  2. Java 9 Support
  3. Declarative Services
  4. The JAX-RS Whiteboard
Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when it's available.

An evening of OSGi with BGJUG on April 17 Sofia, Bulgaria

The BGJUG and Software AG are kindly hosting an Evening of OSGi on Tuesday April 17 from 7pm.

The OSGi Alliance is in Sofia conducting its Expert Group face to face meetings and Board meeting so this evening event will provide an excellent opportunity to meet with the local Java community while we are in town.

There are two talks lined up from the OSGi Alliance:

  • the first is an 'Introduction to OSGi' by BJ Hargrave (OSGi Alliance CTO) and 
  • the second is a look at 'How OSGi Alliance specifications have influenced the IoT market' by Pavlin Dobrev & Kai Hackbath (Bosch). 

In addition Todor Boev from Software AG will be presenting 'OSGi and JPMS in practice' which will discuss Software AG's experiences with OSGi and Jigsaw/JPMS now that Java 9 is released.

The event is being held at Sofia Tech Park / Incubator building, Tsarigradsko shoes 111B, 1784 Sofia [MAP].

Please be sure to register in advance to secure your place.

Thanks to BGJUG and Software AG for their assistance in arranging this event and of course to the speakers for their support.

We hope you can join us.

Tuesday, March 27, 2018

OSGi R7 Highlights: The JAX-RS Whiteboard

OSGi R7 is more than just an update to the existing OSGi specifications, it also includes a host of new specification chapters adding modularity to Java technologies. Today I want to tell you about about the exciting new support for RESTful services using the JAX-RS Whiteboard specification.

OSGi has long offered support for the web using Servlets, but these can be cumbersome for creating modern APIs which may need to represent data in a variety of ways. JAX-RS streamlines these sorts of applications by separating the routing of requests and serialization of entities from the business logic of your application.

JAX-RS: The Basics

A JAX-RS application uses resources to handle incoming requests. The method used to handle an incoming request is determined using annotations.
public class Upper {
  @Path("rest/upper/{param}")
  @GET
  public String toUpper(@PathParam("param") String param) {
    return param.toUpperCase();
  }
}

JAX-RS resources are then grouped together (along with extension services) in an Application which is hosted by the server runtime. This model is very similar to that used by Servlets in a WAR file, and so unsurprisingly the best way to use JAX-RS resources in an OSGi framework is very similar to the way you would use a Servlet!

The JAX-RS Whiteboard

When in an OSGi framework, we want our applications to be modular and loosely coupled. The best way to contribute a JAX-RS resource is therefore to register it as a service with the JAX-RS whiteboard. Using the new Declarative Services Component Property Types makes this really easy!

@Component(service=Upper.class)
@JaxrsResource
public class Upper {
    @Path("rest/upper/{param}")
    @GET
    public String toUpper(@PathParam("param") String param) {
        return param.toUpperCase();
    }
}

Note that JAX-RS doesn't impose any type restrictions on the resource object, so the whiteboard doesn't either. Any service registered with the "osgi.jaxrs.resource" property set to true (provided here by the @JaxrsResource component property type) will therefore be picked up by the whiteboard and used as a resource.

In addition to individual resources the whiteboard can also host entire JAX-RS Application instances:

@Component(service=Application.class)
@JaxrsApplicationBase("myApp")
public class MyApp extends Application {
    ...
}

These applications are isolated from one another, allowing multiple bundles to contribute RESTful APIs without fear of conflict.

Extending the JAX-RS Whiteboard

A key feature of the JAX-RS model is its extensibility. Support for new media types, content encodings, custom exception mapping and many other facilities are provided using extension types. These extension types are also supported by the JAX-RS whiteboard and can be registered just as easily as any other types!

@Component
@JaxrsExtension
@Produces("application/vnd.acme.foo")
public class MyExt implements MessageBodyReader,
                              MessageBodyWriter {
   ...
}

In summary

The JAX-RS whiteboard offers a powerful, flexible, easy-to-use model for creating RESTful APIs for your OSGi bundles. The Reference Implementation at Apache Aries is available now for you to try out, and there are examples for developing JAX-RS whiteboard applications at OSGi enRoute. More information (and a lot of additional features) are described in the R7 specification. Have Fun!


Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:

  1. Proposed Final Draft Now Available
  2. Java 9 Support
  3. Declarative Services
Coming up next is The JAX-RS Whiteboard. Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when its available.

Wednesday, March 14, 2018

OSGi R7 Highlights: Declarative Services

The OSGi Compendium Release 7 specification contains version 1.4 of the Declarative Services specification which includes a number of exciting new features.

If you are not familiar with Declarative Services (DS), it is the dependency injection model for OSGi programming that fully supports OSGi modularity and services and the dynamic nature of both. Declarative Services uses a declarative model for publishing, finding and binding to OSGi services. This model simplifies the task of authoring OSGi services by performing the work of registering the service and handling service dependencies. This minimizes the amount of code a programmer has to write; it also allows service components to be loaded only when they are needed. As a result, bundles need not provide a BundleActivator class to collaborate with others through the service registry.

Declarative Services was introduced as an OSGi specification in 2005 supporting method injection. DS has been updated to add an annotation-based programming model in version 1.2 and to add support for field injection,  component property types, and introspection in version 1.3. Version 1.4 of DS now adds support for constructor injection, activation fields, enhancements to component property types, and a number of smaller enhancements.

Service Component


A Service Component is a Java class which can be optionally registered as an OSGi service and can optionally use OSGi services. The runtime for DS is called Service Component Runtime (SCR) and uses component descriptions in the bundle (in XML form) to instantiate and wire components and services together. The component descriptions are used by SCR as a performance choice so that SCR can avoid processing all classes in a bundle to search for annotations at runtime. But writing XML is not really great for developers, so DS provides annotations which can be used in source code. Tooling, such as Bnd, is then used to generate the component description XML from the annotations during bundle building. So when the following example service component is included in a bundle by Bnd, Bnd will process the annotations and generate the component description XML for the component in the bundle.

@Component
public class ExampleImpl implements Example {
 @Reference(service=LoggerFactory.class)
 private Logger logger;
 @Override
 public void say(String message) {
  logger.info(message);
 }
}

<scr:component name="example.provider.ExampleImpl"
  xmlns:scr="http://www.osgi.org/xmlns/scr/v1.3.0">
  <implementation class="example.provider.ExampleImpl"/>
  <service>
    <provide interface="example.api.Example"/>
  </service>
  <reference field="logger"
    interface="org.osgi.service.log.LoggerFactory"
    name="Logger"/>
</scr:component>

Constructor Injection


Prior to 1.4, DS supported method injection:

private Example example;
@Reference
void setExample(Example example) {
 this.example = example;
}

and field injection:

@Reference
private Example example;

New to DS 1.4 is support for constructor injection:

private final Example example;
@Activate
public ExampleImpl(@Reference Example example) {
 this.example = example;
}

The Activate annotation is used to mark the constructor to be used by SCR for constructor injection. By default, SCR will use the public no-parameter constructor. Since, by their nature, constructors can only be called once per object instance, only static policy references can be used for constructor injection. For dynamic policy references, you will need to use either field or method injection.

Both scalar and multiple cardinality references are supported for constructor injection. Here is an example of multiple cardinality where the type of the referenced service is determined from the generic signature of the parameter.

private final List<Example> examples;
@Activate
public ExampleImpl(@Reference List<Example> examples) {
 this.examples = examples;
}

In addition to injecting the service objects for referenced services, you can also inject objects related to referenced services just as you can also do for method and field injection.

@Activate
public ExampleImpl(
  @Reference
  ServiceReference<Example> sr,
  @Reference
  ComponentServiceObjects<Example> so,
  @Reference(service=Example.class)
  Map<String,Object> serviceProps,
  @Reference
  Map.Entry<Map<String,Object>,Example> tuple) {
    …
}

Finally, constructor injection also supports passing activation objects to the component constructor. Activation objects are the objects you can pass to the activate method. Any constructor parameter not annotated with Reference must be an activation object.

public @interface Props {
 int my_port() default 8080;
}
@Activate
public ExampleImpl(
  ComponentContext context,
  BundleContext bc,
  Map<String,Object> componentProps,
  Props props) {
    …
}

Activation Fields


In addition to supporting injecting activation objects using constructor injection, and also method injection via the activate method, DS 1.4 adds support for field injection of activation objects. A field of the type of an activation object can be annotated with Activate and SCR will inject the activation object into the field after object construction.

@Activate
private ComponentContext context;
@Activate
private BundleContext bc;
@Activate
private Map<String,Object> componentProps;
@Activate
private Props props;

Component Property Type Enhancements


Component property types were added to DS in version 1.3. A component property type is an annotation that is used to both define and consume component properties in a type safe manner. For example, we can define the following component property type:

public @interface Props {
 int my_port() default 8080;
}

When this component property type is used in a component:

@Activate
private Props props;

it both defines the component property named my.port (the annotation element names are mapped to component property names) and sets its value to 8080 in the generated component description. SCR will also generate an instance of the component property type which can be injected into the component instance and then be used to access the component property in a type safe manner.

int my_port = props.my_port();

And since DS supports type conversion for the component property value, even if the component property value was a string value of "8080", your component implementation does not need to worry.

Lets say you wanted to set the my.port component property for your component to a value other than 8080 and could not change the declaration of the component property type (because other components might also use the same component property type), in DS 1.3 you would have to use a non-type-safe string:

@Component(property={"my.port:Integer=9080"})

New for DS 1.4 is the ability to also use component property types to annotate components and set property values in a type-safe manner. To use a component property type in this way, you must annotate the component property type with ComponentPropertyType. This meta-annotation lets Bnd know the annotation on the component class must be processed as a component property type for setting component property values.

@ComponentPropertyType
public @interface Props {
 int my_port() default 8080;
}
@Component
@Props(my_port=9080)
public class ExampleImpl implements Example {
  @Activate
  private Props props;
  …
}

Now when Bnd processes the component during bundle creation, the generated component description will set the value of the my.port component property to 9080.

There is also special support for component property types which are single-element annotations and marker annotations. For a single-element annotation, the component property name for the value element is derived from the component property type name instead of the element name value. For a marker annotation, a component property name is derived from the component property type name, just as for single-element annotations, and the component property value is Boolean.TRUE. Marker annotations are only useful for setting a component property to the value true. They cannot be used to get a property value since they have no elements to call from your component implementation.

Sometimes component property names can be long. For example, osgi.http.whiteboard.servlet.name. In order to make using component property types more friendly in source code, DS supports the use of the PREFIX_ final field so that shorter element names can be mapped to longer component property names. For example, if we need the component property name com.acme.server.port and we don't want to name the element com_acme_server_port or name the single-element annotation ComAcmeServerPort, we can use the PREFIX_ final field to add a name prefix to the mapped component property name.

@ComponentPropertyType
public @interface ServerPort {
  String PREFIX_ = "com.acme."; 
  int value() default 8080; // com.acme.server.port property
}

This new support for component property types is so useful, you will see we have defined quite a number of them throughout many of the R7 specifications to make using those specifications with DS much nicer. The DS spec defines some component property types for basic service properties and both the updated Http Whiteboard and the new JAX-RS Whiteboard specifications define component property types specific to their specifications.

And There is More


I just touched on, what I feel, are the major updates for Declarative Services in the new version 1.4 specification which is part of OSGi Compendium Release 7. Make sure to check out the Changes section in the spec to see all the updates for 1.4.

PS. While the DS 1.4 specification is done, tooling and runtimes which support all of these new features may still be under development (at the time of this writing).



Want to find out more about OSGi R7?

This is one post in a series of 12 focused on OSGi R7 Highlights.  Previous posts are:

  1. Proposed Final Draft Now Available
  2. Java 9 Support
Coming up next is The JAX-RS Whiteboard.  Be sure to follow us on Twitter or LinkedIn or subscribe to our Blog feed to find out when its available.