DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.

DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!

Launch your software development career: Dive head first into the SDLC and learn how to build high-quality software and teams.

Open Source Migration Practices and Patterns: Explore key traits of migrating open-source software and its impact on software development.

Related

  • The ABCs of Unity's Coroutines: From Basics to Implementation
  • In-Depth Guide to Using useMemo() Hook in React
  • Spring Beans With Auto-Generated Implementations: How-To
  • How to Secure Apache Ignite From Scratch

Trending

  • A Look Into Netflix System Architecture
  • How to Submit a Post to DZone
  • DZone's Article Submission Guidelines
  • Spring AI: How To Write GenAI Applications With Java

OSGi: Declarative Services and the Registry

Meet declarative services and see three ways to get them into the OSGi Service Registry.

By 
Alan Hohn user avatar
Alan Hohn
·
Nov. 04, 16 · Tutorial
Like (3)
Save
Tweet
Share
15.0K Views

Join the DZone community and get the full member experience.

Join For Free

In two previous articles, I introduced building an OSGi bundle and the architecture of a multi-bundle OSGi solution. One of the key features of that multi-bundle solution in its associated GitHub repository is the use of OSGi declarative services.

OSGi declarative services are the OSGi way to handle the instantiation problem: the fact that we want to code to interfaces, but we need some way to instantiate classes and some way to provide some concrete instance of an interface in order for the parts of our modular application to work together.

Like most solutions of this type, there are three parts to OSGi services: the service interface, the service registry, and the service implementation. This is exactly the same design as:

  • Spring: The service interface is used as the type for the setter method or constructor; the service registry is the application context, and the service implementation is the bean.
  • JDBC: The service interface is the JDBC API itself, the service registry is the JDBC driver manager, and the service implementation is the driver.
  • JNDI/EJB: The service interface is whatever type we cast to once we do the lookup, the context is the service registry, and the EJB is the service implementation.

Of course, the reason all these use the same design pattern is because this is the minimum number of things needed for discoverable services. But as a key aside, we can conclude that as long as we code to Plain Old Java Interfaces, and separate out instantiation and injection of those interfaces, we can write code that works perfectly well in OSGi, Spring, and Java Enterprise. (Of course, things like database access, remote lookup, and transactions break that pure "framework independence" a little bit, but it still applies to a lot of our code.)

Coding to Interfaces

For our OSGi declarative services example, we first create our own Plain Old Java Interface:

public interface Greeter {
    String greet();

}


We then write some "manager" code that uses that interface:

// ...
public String greet(String language) {
    // ... Fetch the right greeter
    return greeter.greet();
    // ...
}
// ...


Note that in our example, these are in separate bundles, so we have to export the api package from the interfaces bundle and import it into the manager bundle in order for the interface to be visible.

Declaring a Service

To make an actual instance of this service, we write ordinary Java code that implements the interface:

public class FrenchGreeter implements Greeter {
    private static final Logger LOGGER = LoggerFactory.getLogger(FrenchGreeter.class);
    @Override
    public String greet() {
        LOGGER.info("Le 'greeter' en francais!");
        return "Bonjour tout le monde!";
    }
}


So far none of these examples include anything OSGi-specific. To use this service in an OSGi context, we need to do two things: tell the service registry about the implementation, and have a way to lookup the implementation from the registry where we need it.

There are a few ways to register a service with the OSGi service registry.

Bundle Activator

First, we could register the service programmatically. To do this, we need a reference to the service registry, and a way to tell the OSGi container to invoke some code for us when it starts our bundle. We can get both of these with a bundle activator. If we write a class that implements this interface, and tell our Maven Bundle plugin about it so it gets configured in the META-INF/MANIFEST.MF file of our JAR, then OSGi will invoke our class after our bundle is started and before it is stopped. We can use the BundleContext it passes us to register our service. To be polite, we should also unregister our service when we are stopped.

It might look something like this (on the start side):

public class Activator implements BundleActivator {
    public void start(BundleContext context) throws Exception {
        Dictionary<String,String> props = new Dictionary<>();
        props.put("language", "fr");
        context.registerService(Greeter.class.getName(),
            new FrenchGreeter(), props);
    }
    // ... stop
}


The upside to this method is that it's very clear. The downside is that we're writing boilerplate code that's OSGi-specific in every bundle with a service implementation.

Blueprint

OSGi also supports XML configuration that is very similar to what's supported by the Spring Framework. If we drop an XML file into OSGI-INF/blueprint inside our bundle JAR file, then the OSGi container will parse it automatically when our bundle is started.

I'll show a Blueprint XML example in more detail in a future article, but here's a quick look at what it might look like:

    <bean id="frenchGreeter" class="org.anvard.karaf.greeter.french.FrenchGreeter">
    </bean>
    <service id="frenchGreeterService" ref="frenchGreeter"
        interface="org.anvard.karaf.greeter.api.Greeter">
        <service-properties>
            <entry key="language" value="fr" />
        </service-properties>
    </service>


The advantage of this is that it avoids boilerplate Java, but it includes boilerplate XML. If we're not otherwise using Blueprint XML we might not want to bring it in just for this purpose.

Service Component Runtime (SCR)

Since our example is using Apache Karaf, which is built on the OSGI capabilities of Apache Felix, we have SCR available to us, including Java annotations. This means we can annotate our implementation class and have it automatically discovered by Felix and registered as a service.

The resulting code looks like this:

@Component(immediate = true)
@Service
@Property(name="language", value="fr")
public class FrenchGreeter implements Greeter {

    private static final Logger LOGGER = LoggerFactory.getLogger(FrenchGreeter.class);

    @Override
    public String greet() {
        LOGGER.info("Le 'greeter' en francais!");
        return "Bonjour tout le monde!";
    }
}


This is nice because it is very self-contained. We do pay the cost of having Felix-specific annotations in our code, so we're no longer neutral as to whether we're using OSGi and which container we're using.

Wrapping Up

Of course, Java annotations by themselves don't do anything, and we also need to have a way to look up the service once it's in the registry. Next time I'll cover both of these topics.

Interface (computing) Implementation

Opinions expressed by DZone contributors are their own.

Related

  • The ABCs of Unity's Coroutines: From Basics to Implementation
  • In-Depth Guide to Using useMemo() Hook in React
  • Spring Beans With Auto-Generated Implementations: How-To
  • How to Secure Apache Ignite From Scratch

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: