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
Please enter at least three characters to search
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

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Introduction to Apache Kafka With Spring
  • Streamlining Event Data in Event-Driven Ansible
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • APIs for Logistics Orchestration: Designing for Compliance, Exceptions, and Edge Cases

Trending

  • A Deep Dive Into Firmware Over the Air for IoT Devices
  • MySQL to PostgreSQL Database Migration: A Practical Case Study
  • Understanding and Mitigating IP Spoofing Attacks
  • Enhancing Security With ZTNA in Hybrid and Multi-Cloud Deployments
  1. DZone
  2. Software Design and Architecture
  3. Security
  4. OSGi Dependency Injection

OSGi Dependency Injection

This overview of OSGi Dependency Injection covers its use and benefit, the whiteboard pattern, and some general advice for DI.

By 
Amit Kumar Mondal user avatar
Amit Kumar Mondal
·
Updated Jun. 02, 17 · Tutorial
Likes (13)
Comment
Save
Tweet
Share
23.6K Views

Join the DZone community and get the full member experience.

Join For Free

OSGi follows a standard service model paradigm. This is required because Java shows how hard it is to write collaboratively with only class sharing. The standard solution in Java is to use factories that use dynamic class loading and statics. For example, if you want a Factory, you call the static factory method Factory.newInstance(). Behind that façade, the newInstance methods try every class loader trick to create an instance of an implementation subclass of the BuilderFactory class.

The solution to all these issues is simply the OSGi service registry. A bundle can create an object and register it with the OSGi service registry under one or more interfaces. Other bundles can go to the registry and list all objects that are registered under a specific interface or class. For example, a bundle provides an implementation of the Builder. When it gets started, it creates an instance of its BuilderFactoryImpl class and registers it with the registry under the BuilderFactory class. A bundle that needs a BuilderFactory can go to the registry and ask for all available services with the BuilderFactory class. Even better, a bundle can wait for a specific service to appear and then get a callback.

A bundle can, therefore, register a service, get a service, and listen for a service to appear or disappear. Any number of bundles can register the same service type, and any number of bundles can get the same service. This is depicted in the following figure.

alt text

Well-Known Dependency Injection Methods

  1. Declarative Service
  2. ServiceTracker and ServiceTrackerCustomizer
  3. Usual way of getting ServiceReferences
  4. ServiceListener
  5. ServiceFactory
  6. iPOJO
  7. Felix Dependency Manager

Whiteboard Pattern

The whiteboard pattern leverages the OSGi framework’s service registry instead of implementing a private registry as required by the listener pattern. Instead of having event listeners track event sources and then register themselves with the event source, the whiteboard pattern has event listeners register themselves as a service with the OSGi framework. When the event source has an event object to deliver, the event source calls all event listeners in the service registry.

Remarkably, the event source is not registered with the framework as a service. This makes bundles using the whiteboard pattern significantly smaller and easier to implement. The inter-bundle dependency between the event source and the event listener is handled by the framework and requires almost no code in the event source and event listener bundles.

alt text

The following Dependency Injection methods follow a whiteboard pattern

  1. Declarative Services
  2. ServiceTracker and ServiceTrackerCustomizer
  3. ServiceFactory

Declarative Services

Declarative Services make writing a service implementation as simple as writing a POJO with a few annotations. Though there are other systems that do injections similar to Declarative Services, these other systems ignore time and dependencies. By handling time and (dynamic) dependencies without any code overhead, OSGi provides a toolbox that is as innovative as objects were in the 90s.

ServiceTracker Useful Methods

public class ServiceTracker < S, T > implements ServiceTrackerCustomizer < S, T > {
    .....
    public void open() {..
    }
    public void open(boolean trackAllServices) {..
    }
    public T getService() {..
    }
    public Object[] getServices() {..
    }
    public T waitForService(long timeout) throws InterruptedException {..
    }
    public void close() {..
    }
    ....
}


public interface ServiceTrackerCustomizer < S, T > {
    public T addingService(ServiceReference < S > reference);
    public void modifiedService(ServiceReference < S > reference, T service);
    public void removedService(ServiceReference < S > reference, T service);
}


ServiceTracker Example

public final class ServiceTrackerExample {

    private ServiceTracker logTracker;

    void init() {
        logTracker = new ServiceTracker(bc, LogService.class.getName(), null);
        logTracker.open();
    }

    LogService getLog() {
        return (LogService) logTracker.getService();
    }

    void test() {
        getLog().doLog(...);
    }

    void destroy() {
        logTracker.close();
    }

}


ServiceReference Example

public final class ServiceReferenceExample {

    private BundleContext bc = ...;

    void doSomething() {
        final ServiceReference sr = bc.getServiceReference(HttpService.class.getName());
        if (sr != null) {
            final HttpService http = (HttpService) bc.getService(sr);
            if (http != null) {
                http.registerServlet(....)
            }
        }
    }

    void destroy() {
        bc.ungetService(sr)
    }

}


Quirks

These APIs can easily be misused if proper precautions have not be taken care of. A few examples have been demonstrated as follows.

Example 1

public final class ServiceReferenceExample {

    private final BundleContext bc;

    public ServiceReferenceExample() {
        bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
    }

    void doSomething() {
        final ServiceReference sr = bc.getServiceReference(HttpService.class.getName());
        if (sr != null) {
            final HttpService http = (HttpService) bc.getService(sr);
            if (http != null) {
                http.registerServlet(....);
            }
        }
    }

    void destroy() {
        // bc.ungetService(sr)
    }

}


The getService(..) call increases an HttpService use counter of this bundle. OSGi Service Registry keeps track of the service use counters to optimize service injections and cleanups. If we forget to call ungetService(..), the service counter will never be decremented and it will result in optimization issues.

A bundle’s use of a service object obtained from this getService(..) method is tracked by the bundle’s use count of that service. Each time the service object is returned by getService(..), the context bundle’s use count for the service is incremented by one. Each time the service object is released by ungetService(..) the context bundle’s use count for the service is decremented by one.

Example 2

public final class ServiceReferenceExample {

    private final BundleContext bc;
    private ServiceReference < HttpService > sr;

    public ServiceReferenceExample() {
        bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
    }
    void doThis() {
        sr = bc.getServiceReference(HttpService.class.getName());
        if (sr != null) {
            final HttpService http = (HttpService) bc.getService(sr);
            if (http != null) {
                http.registerServlet(....);
            }
        }
    }
    void doThat() {
        if (sr != null) {
            final HttpService http = (HttpService) bc.getService(sr);
            if (http != null) {
                http.registerServlet(....);
            }
        }
    }
    void destroy() {
        bc.ungetService(sr);
    }

}


In this example, multiple calls to getService(..) result in the service use counter to be incremented by 1 and finally, the service count will be 2 in this example. The call to ungetService(..) will only decrement the service use counter by 1.

Example 3

public final class ServiceReferenceExample {

    private final BundleContext bc1;
    private final BundleContext bc2;

    public ServiceReferenceExample() {
        bc1 = FrameworkUtil.getBundle(getClass()).getBundleContext();
        bc2 = FrameworkUtil.getBundle(A.class).getBundleContext(); //A.java in other bundle and has been imported
    }

    void doSomething() {
        final ServiceReference sr = bc1.getServiceReference(HttpService.class.getName());
        if (sr != null) {
            final HttpService http = (HttpService) bc1.getService(sr);
            if (http != null) {
                http.registerServlet(....)
            }
        }
    }

    void destroy() {
        bc2.ungetService(sr)
    }

}


In this example, the BundleContext that invokes getService(..) will have a service count of 1 for HttpService. On the other hand, the BundleContext that calls ungetService(..) will not result in decrementing the service count as the service count is already 0 for the bundle in which A.java resides in. And the service count that invokes getService(..) will remain 1.

Example 4

public final class ServiceReferenceExample {

    private volatile HttpService httpService;

    public ServiceReferenceExample() {
        final ServiceTracker << ? , ? > serviceTracker = new CustomServiceTracker(
            Activator.getBundleContext());
        serviceTracker.open();
    }
    void doSomething() {
        ...
    }
    private final class CustomServiceTracker extends ServiceTracker {

        public DbManagerServiceTracker(BundleContext context) {
            super(context, HttpService.class.getName(), null);
        }

        @Override
        public Object addingService(ServiceReference reference) {
            httpService = (HttpService) context.getService(reference);
            return httpService;
        }

        @Override
        public void removedService(ServiceReference reference, Object service) {
            context.ungetService(reference);
            dbManager = null;
        }
    }
}


Here's a typical usage that developers do quite often. It could have simply be done by calling getService() on the ServiceTracker. It is not easily maintainable to extend ServiceTracker. We must implement ServiceTrackerCustomizer if and only if we need specific operations to be performed while tracking services.

Example 5

public final class ServiceReferenceExample {

    private volatile DBManager dbManager;

    public ServiceReferenceExample() {
        final ServiceTracker << ? , ? > serviceTracker = new ServiceTracker(
            Activator.getBundleContext(), DBManager.class.getName(), null);
        serviceTracker.open();
    }

    void doSomething() {
        dbManager = serviceTracker.getService();
        dbManager.save()
    }

}


If the ServiceTracker is not required anymore to track any changes, it is a good practice to close it.

Best Practices (According to Me and Subject to Change)

  1. Always unget ServiceReference after you get the service instance.
  2. Unget the ServiceReference on the same BundleContext that requested it.
  3. Be careful to call getServiceReference() on BundleContext as it returns the first found ServiceReference. Call getServiceReferences() instead.
  4. Programmer’s nightmare: Multiple Threads request service on the same BundleContext. In this case, the thread shares the same BundleContext instance.
  5. Always close the ServiceTracker if you don’t need to continue listening.
  6. Don’t use multiple BundleContexts to request ServiceReferences.
  7. If service consumption must not be manipulated on demand, switch to Declarative Services or Felix DM.
  8. ServiceTracker can listen to multiple instances of the requested service. It is, therefore, better to call getServiceReferences() instead as you don’t 9. know how many services will be tracked (unless you are 100% sure).
  9. Once you call, getServiceReferences() on ServiceTracker, then follow #1 to #3.
  10. Don’t override ServiceTracker. Implement ServiceTrackerCustomizer instead. It is more consumer-friendly.
Dependency injection Event

Published at DZone with permission of Amit Kumar Mondal, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Introduction to Apache Kafka With Spring
  • Streamlining Event Data in Event-Driven Ansible
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • APIs for Logistics Orchestration: Designing for Compliance, Exceptions, and Edge Cases

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and 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:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!