Showing posts with label component design. Show all posts
Showing posts with label component design. Show all posts

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.

Tuesday, September 4, 2007

SOA & OSGi

Nine years ago, when we started working on the OSGi specifications, we used the word service to describe the object that acted as a conduit between bundles. Today, Service Oriented Architectures (SOA) are hot and every software vendor seems intent on confusing a muddy picture even further by bringing their products under this wide umbrella. The result is that many people file OSGi under webservices, as the most popular exponent of SOA, and then conveniently ignore any further documentation because they already know what web services are. As an example, a friend of mine consistently gets his papers rejected because many of the reviewers make this very false assumption.

A bit of history. In 1985, I was developing systems for the newspaper market and we just started our Magnus Opus: a newspaper page makeup terminal (this was the time that a 25 Mhz 286 was state of the art). This product was of strategic importance for our company and I did due diligence on a lof of languages to select the best one for this project. After discovering Smalltalk/V 286, I fell head over heels in love. I thought object oriented (OO) software was the best thing since sliced bread! I never had been comfortable with the common design methodologies at that time called Structured Design. Discovering OO felt like coming home after a long journey. After grasping the technology I made my second worst advice ever (the worst was when I told a programmer friend in 1994 that building an e-commerce shopping cart application was a silly idea, sorry Matts). I told my manager that if we started to build our systems using objects we would be able to get fantastic reuse after a year or so. Obviously under the condition that he allowed me to go to all these cool conferences like the OOPSLA. I assured him, new applications could be build in days instead of years, just using objects ...

Well, as most of us know it did not turn out this way. Yes, we got a lot of advantages using OO technology but reuse turned out a lot harder than we thought. Sadly, I think the key cause of the lack of reuse was the violation of the two sacred rules of Structured Design: low coupling, high cohesion. It was very much like a couple of girls on the beach trying desperately not to expose themselves to the boys in front of them while changing into their swimming gear; not realizing that there were also some boys sitting behind them. We were so busy hiding our data in instance variables writing get and set methods that we never realized that we were exposing the object structure to anybody using our classes. That is, if you do new Foo() from class Bar, you just coupled the classes Foo and Bar for eternity. Trying to reuse Bar immediately drags in Foo. Now if Foo and Bar had been cohesive (meaning they are really parts of the same function) this would not be such a problem. However, because we were so busy writing get/set methods we mostly ended up with systems that had transitive dependencies that basically included all classes in your company's repository. If you want to see an example, write a "Hello world" program with an empty local Maven repository. Not only made this coupling reuse very hard, the coupling made making changes also very complicated because many changes were far reaching.

At the end of the previous millennium our industry learned many hacks to reduce this structural coupling; the most (in)famous is the Factory pattern. This pattern allowed you to manage the coupling outside the original program by moving the coupling to a runtime decision. However, it is a hack. OO languages have no built-in answer to this problem, just look at the large number of attempts one can find in Java to solve this coupling problem.

Also, if you look at one of the great insights of the last decade, Inversion of Control (IoC), you see that this is really about decoupling. IoC (or the Hollywood principle of "do not call us, we will call you") takes an object and provides it with its dependencies instead of this object trying to create its dependencies. This simple idea allows for POJOs, objects that are minimally coupled.

Services are trying to solve the same problem: minimize complexity by decoupling. The key insight that lead to the service model is that the OO has an important role in modularization on the small scale but we need something of a bigger granularity on the larger scale. A modularity that minimizes coupling to well defined conduits between coarse components. This was exactly the problem we tried to solve since that 1998 meeting in the Research Triangle Park in Raleigh where the technical OSGi work got started. Our key assignment was to let software from many different service providers collaborate in a single residential gateway. Trying to standardize large class libraries with highly coupled frameworks would have taken forever and it would have done a terrible job. By focusing on the coarse components (the bundles) that would collaborate through objects named by their interface that were found in a registry (the services) we created a model that had minimal, well defined, and documented coupling between bundles.

The key advantage of SOA is therefore the reduced coupling between the blocks of a system. The model became wide known with Webservices because Microsoft and IBM decided to put a lot of marketing dollars in that direction, making SOA almost synonymous with XML, SOAP, HTTP, some other acronyms, and associated distributed computing problems. It is not. Service Oriented Architectures reduces the complexity caused by not managing structural coupling by providing well defined conduits between components. Webservices achieve this using a myriad of acronyms, OSGi technology achieves this by using an extremely low overhead in-VM service publish/find/bind model.

So the key question is if the confusion between Webservices and OSGi is doing us harm? I think it does, many people fail to look further because they assume they understand. For example, I am flabbergasted that leading software magazines like ACM Communications and IEEE Software never had an in-depth article about OSGi technology because I strongly believe that the ramifications of this technology for our industry are profound.

Peter Kriens