1

Disclaimer: I'm just getting started with OSGI so please bear with me and my lack of knowledge...


For the sake of this exercise, suppose I have a Jersey (2.17.0) based REST application running under Jetty (9.2.10) in an OSGI environment, which provides the user with some statistics that are gathered from a separate server via SOAP.

I'm now trying to reuse some of the existing code in a different application which is supposed to retrieve statistics via JMS (or something else).

My intention is to introduce an abstraction layer for the client API in a bundle, implement a bundle for each application with the appropriate communication channel and use CDI to inject the client at runtime in my REST resources. For example:

The REST resource (bundle 1, common for both apps)

@Path("statistics")
public class StatisticsResource {

    @Inject
    private StatisticsClient client;

    @GET
    @Path("users")
    public List<User> getActiveUsers(){
        return client.getActiveUsers();
    }
}

Common API (bundle 2, common for both apps)

public interface StatisticsClient {
    List<User> getActiveUsers();
}

SOAP implementation (bundle 3 for app 1)

@ApplicationScoped
public class SOAPClient implements StatisticsClient {
    @Override
    public List<User> getActiveUsers() {
        // connect to server via SOAP
    }
}

JMS implementation (bundle 3 for app 2)

@ApplicationScoped
public class JMSClient implements StatisticsClient {
    @Override
    public List<User> getActiveUsers() {
        // connect to server via JMS
    }
}

I've been reading and searching for information on how to use injection with Jersey, HK2 (2.4.0) and OSGI but I so far I haven't found something relevant to match the above idea.

Most of the Jersey CDI injection examples I've seen so far, define bindings using concrete classes, such as bind(MyService.class).to(MyService.class); whereas I'd like to be able to switch implementation at runtime and use either SOAPClient or JMSClient depending on the application where the code is currently running. Ideally the implementation would be deduced/detected by the framework from the available OSGI services (or perhaps classpath or something similar)...

Is that doable, and if so, what am I missing? Alternatively what basic concept did I perhaps misunderstood or missed?

Community
  • 1
  • 1
Morfic
  • 15,178
  • 3
  • 51
  • 61
  • Haven't done this before and would need time to try, so here just a comment: You could try to use [HK2Utilities](http://stackoverflow.com/questions/23402814/how-does-servicelocator-find-service-and-contact-automatically-in-hk2/35191749#35191749) to bind Services/Contracts at runtime *(works fine)*. "Unbinding" should be able by calling `shutdown()` on your injected ServiceLocator instance. Than bind again. **Note:** I have no idea if this works, but will try it myself soon and if not delete the comment. **Suggestion:** Give it a try :) – zyexal Nov 08 '16 at 15:38
  • I'm not understand exactly what the problem is here. You can always conditionally do bind(SOAPClient.class).to(StatisticsClient.class) or bind(JMSClient.class).to(StatisticsClient.class) in your registration code based on available OSGi services or whatever other criteria you have. Is that your question? – jwells131313 Nov 09 '16 at 12:47
  • @jwells131313 I would like to avoid binding classes _manually_ if at all possible. If you're familiar with spring annotation config, then you know you can work throughout your code with an _injected_ interface (contract), and simply annotate your implementations with `@Service`, and at runtime the framework will detect which class implements your service, injecting it where it is required without you having to do anything else. I'm aware that the environment is different in this case, and that's why I'm wondering whether I've missed something... – Morfic Nov 09 '16 at 13:05
  • @zyexal thanks for your suggestion, although I'm a bit reluctant using a relatively new and unproven library in production (no offence intended towards its author or anyone else). Furthermore I may not be allowed to use it because of other environment restrictions even if I wanted to. Thank you nonetheless – Morfic Nov 09 '16 at 13:10
  • There is no magic happen inside that lib. Also it's very small. Have a look inside and you will know what to do (I guess) – zyexal Nov 09 '16 at 13:14
  • You may then want to use the @Service and the hk2-metadata-generator in your build API like https://hk2.java.net/2.5.0-b28/apidocs/org/glassfish/hk2/utilities/ServiceLocatorUtilities.html#createAndPopulateServiceLocator. Another place you might want to start looking is here: https://hk2.java.net/2.5.0-b28/getting-started.html. If you would like you can PM me and we can talk it out more – jwells131313 Nov 09 '16 at 13:35
  • @jwells131313 thanks, that may be what I had overlooked and seems to be in close relation to the library zyexal was suggesting. I'll take a closer look as soon as I finish the task at hand and let you guys know. Hopefully I won't have to take you up on your PM offer :) – Morfic Nov 09 '16 at 14:13

1 Answers1

0

You can use simple EJBs with CDI producers(producers inject beans, or produces bean instances to be injected, depending on some criteria).

pseudo code:

public interface Service;

@Stateless
@ServiceImplementation(A)  //Qualifier *
public class ServiceImplA implements Service{
    //impl code
}

@Stateless
@ServiceImplementation(B)    //Qualifier *
public class ServiceImplB implements Service{
    // impl code
}

@Produces 
@ApplicationScoped
public Service produceServiceInstances(){
    if(condition)
       return ServiceImplA
    else
       return ServiceImplB
}

Check this link https://docs.oracle.com/javaee/7/tutorial/cdi-adv003.htm

Najeeb Arif
  • 404
  • 3
  • 11