views:

937

answers:

2

It seems natural that a HttpServlet running in OSGi environment (i.e. registered in OSGi HttpService) would want to call some OSGi services to accomplish it's tasks. The question is how to obtain references to these OSGi service inside the servlet.

One way would be to inject dependencies into the HttpServlet instance that is being registered to the OSGi HttpService like this:

MyServlet servlet = new MyServlet();
servlet.setFooService(fooService);

httpService.registerServlet("/myservlet", servlet, initparams, context);

I'm not sure if this is a valid approach since in non-OSGi environment the servlet life-cycle is managed by the Web Container and hence the service reference would not be injected for the servlet instances created later on.

There is another way to solve this when using PAX Web as an implementation of the OSGi HttpService. PAX Web exports the OSGi BundleContext into the ServletContext as a special attribute "osgi-bundlecontext". The BundleContext can then be used to obtain necessary service references:

public void init(ServletConfig servletConfig) throws ServletException {

    ServletContext context = servletConfig.getServletContext()
    BundleContext bundleContext = 
        (BundleContext) context.getAttribute("osgi-bundlecontext");

    ServiceReference serviceRef =
         bundleContext.getServiceReference("com.foo.FooService")
}

However this approach is rather ugly and ties you to a concrete implementation of the OSGi HttpService. Do you know any other (and possibly better) solution to this problem?

A: 

You could inject the services into some object, which is then queried by the servlets.

starblue
This just shifts the problem by one level. How would get the reference to this object from within the servlet?
Pavol Juhos
+1  A: 

If you use a setter for the dependency on the service, as you have shown, it can work outside of OSGi as well. You just need to use some other dependency injection mechanism. If there is none, you could provide a subclass that initializes the servlet using JNDI lookups or from the servlet context.

public class MyServlet_AdapterForMissingDI extends MyServlet{

    public void init(ServletConfig config){
        setFooService(getItFromSomewhere());
    }

}

The point being that if you have DI capabilities that can inject setFooService, you can just use the same servlet in OSGi and elsewhere, if you do not (and still want to support this case), you provide an adapter.

On a related note, check out Felix SCR to configure your object's dependencies, and Pax Web Extender Whiteboard, which takes care of hooking your servlet up with the HttpService.

Specifically, without SCR and Whiteboard, you need to think about the case when the fooService becomes unavailable later, or the HttpService gets started after your servlet. In these cases your servlet would have a reference to a dead service that prevents the bundle from being garbage-collected, or your servlet would not be registered with the HttpService.

Update: Here is the SCR descriptor I use for one of my servlets. SCR handles servlet instantiation, life-cycle, registration (via Whiteboard), and dependencies. There is no OSGi-specific code in the servlet. There is not even the need for a BundleActivator anymore (SCR registers all services):

<component name="oracle.statusServlet" >
<implementation class="mypackage.DataSourceStatusServlet"/>
<property name="service.description" value="Oracle DataSource status servlet" />
<property name="alias" value="/OracleDataSourceStatus" />
<property name="servlet-name" value="Oracle DataSource status servlet" />
<service>
 <provide interface="javax.servlet.Servlet" />
</service>
    <reference name="DATASOURCES" 
            interface="javax.sql.DataSource"
            cardinality="0..n" policy="dynamic" 
            bind="bindDataSource" unbind="unbindDataSource"/>

</component>

The dependencies for the servlet are specified in the reference tag. SCR will do the service lookup and binding.

Thilo
Pax Web Extender Whiteboard looks like a nice solution for registering servlets. Thanks. However the main question is how to obtain service references from within the servlet in OSGi environment. You've mentioned JNDI lookup, but it doesn't feel like the right way to access OSGi service registry. Then you've also mentioned servlet context, but there is no way how to put objects into the servlet context using OSGi HttpService interface. Please correct me if I'm wrong.
Pavol Juhos
You can use SCR to get dependencies inside of OSGi. The point is that you just have a setter (like setFooService) that you use to inject the dependency. That setter can be called manually from a BundleActivator (like you show in your example), by SCR, by iPojo, by Spring, or whatever. These services take care of service tracking.
Thilo
Again, the point is that the servlet itself does not look up its dependencies. It has setter to get them injected from somewhere. You would not use JNDI inside of OSGi. That was an example of how you could make that same servlet work outside of OSGi, in the absence of other DI mechanisms.
Thilo
Okay, I get your point. Only one question remains: You mentioned that SCR handles the servlet life-cycle (by which I mainly mean instantiation). In the standard scenario servlets are instantiated by the Servlet container (e.g. to handle things like "implements SingleThreadModel"). In other words it is common to register servlet only by providing the servlet class not a concrete servlet instance. Is is okay to assume that the implementation of OSGi HttpService will only use the servlet instance passed to registerServlet() method? Specification is not entirely clear about this. Thanks.
Pavol Juhos
The OSGi HttpService will only use the instances that you pass to registerServlet(). I am not sure if it calls init() on them or not.
Thilo