Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registering servlet in Jetty in OSGi environment (Class loading problems)

I have an OSGi application with embedded Jetty 8.1.1 server.

When I run my application as a monolit (no OSGi environment) I can successfuly register servlets by calling org.eclipse.jetty.servlet.ServletContextHandler.addServlet(String className, String mapping) method. So now I'm trying to do the same in OSGi environment.

The goal is to write a bundle (servlet-extender) which would register servlets provided by other bundles when they are started (extender pattern). Therefore after my application starts there are (among others) only two essential bundles running: jetty and servlet-extender.

1st attempt to write servlet-extender:

First I decided to declare servlets with their mappings in MANIFEST.MF file of a bundle providing servlets. If such bundle is started then servlet-extender searches its MANIFEST.MF for servlet mapping declarations. If any servlet mapping declaration was found then servlet-extender called aforementioned ServletContextHandler.addServlet(...) method to actually register the servlet.

While the idea seemed OK there is a problem with class loading. In fact Jetty calls Class.forName("org.my.servlets.MyServletClass").newInstance(). While jetty bundle does not import org.my.servlets package the call Class.forName("org.my.servlets.MyServletClass") fails with ClassNotFoundException.

2nd attempt to write servlet-extender:

I searched for some articles related to class loading in OSGi. This one gave me a hope that I can resolve the previous problem by providing loaded servlet classes through some OSGi service. So I created ServletProvider service with Map<Class<? extends Servlet>, String> getServlets() method (the method just returns servlet classes mapped to some context). Then I modified servlet-extender so that it does not search for servlet mappings in MANIFEST.MF. It now rather wait until some bundle registers ServletProvider implementation. If such service is registered in OSGi service registry then servlet-extender calls its getServlets() method and tries to register returned servlet classes in jetty. While jetty now does not need to call Class.forName("org.my.servlets.MyServletClass") it still have to call servletClass.newInstance() to instantiate servlet. And unfortunatelly it still fails with ClassNotFoundException.

I understand that this can be resolved if jetty bundle would import org.my.servlets package. But this is not possible while jetty bundle is provided by 3rd party and I cannot modify its Import-Package declaration.

How to dynamically register servlets provided by arbitrary bundles?

P.S.: I can't use "OSGi Http Service" because I have to use Jetty's 8.1.1 WebSocketServlet.

like image 526
Michal Vician Avatar asked Dec 28 '25 20:12

Michal Vician


1 Answers

Q: I am still curious about the way of registering servlets declaratively: through servlet class name. Is it possible?

A: Yes :)

First possible solution: if you are allowed to modify the jetty manifest, you could add "DynamicImport-Package: *" to the manifest, this will allow it to load any classes while at the same time it won't be depending on custom org.my.servlets packages. Of course, note that this is bad practice in general for OSGi.

Second possible solution: jetty remains like it is. Servlet bundles list containing servlets declaratively in the manifest, as in your first attempt. The key here is the servlet extender. It will create the servlet and register it in jetty. The important thing about this whole classloading problem is that the creation of the servlet object has to be done somewhere where the classloader (in this case the classloader of the servlet extender) has access to the classes of the servlets. This cannot happen in jetty, because you cannot modify the MF. However, you have full control over the MF of servlet extender, right?

You can either import org.my.servlets (not recommended, since it cannot be foreseen what servlets have to be installed in the future);

or use "DynamicImport-Package: *" in the MF (not recommended practice in general ;) )

or (recommended) the extender loads the classes from the servlet jar using the classloader of the servlet jar - using Bundle loadClass() method (see org.osgi.framework.Bundle). This will load the class correctly. Note that this could be subject to security restrictions.

like image 193
pooh Avatar answered Jan 01 '26 21:01

pooh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!