Instead of introducing, I send the reader to an excellent article
Using Spring in the OSGi-container which served as the starting point for practical study.
So, to the point. Consider the classic version - there is the business logic of the application and it somehow
interacts with the outside world. We use the following link:
client <-> transport <-> receiver <-> serializer / deserializer <-> business logic method.
The serializer begs for a replaceable module, for example, serialization in JSON or serialization in XML.
You can then forget about business logic and focus on a bundle of receiver and serializer.
We use the servlet as the receiver, and for the serializer, for simplicity, we use the implementation of the following interface:
interface ISerializer {
String selialize(Object obj);
}
To bind components, use SpringFramework. Returning again to the
link at the beginning of the article, where the interface injection is based on annotations, the servlet is described something like this
@Component
public class TestServlet extends HttpServlet {
@Autowired
private ISerializer serializer;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// ...
// Object result;
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
out.println(serializer.serialize(obj));
}
}
This is a far-fetched and not the best example of using serialization, but
the binding of the serializer to the servlet is plausible.
It all started on the Apache Felix server with installed gemini-blueprint and spring modules.
[INFO] Started jetty 6.1.x at port(s) HTTP:8080
____________________________
Welcome to Apache Felix Gogo
g! lb
START LEVEL 1
ID|State |Level|Name
0|Active | 0|System Bundle (4.0.1)
1|Active | 1|AOP Alliance API (1.0.0)
2|Active | 1|Apache Log4J (1.2.16)
3|Active | 1|SLF4J API (1.6.1)
4|Resolved | 1|SLF4J Log4J Binding (1.6.1)
5|Active | 1|SLF4J Jakarta Commons Logging Over SLF4J Binding (1.6.1)
6|Active | 1|gemini-blueprint-core (1.0.0.RELEASE)
7|Active | 1|gemini-blueprint-extender (1.0.0.RELEASE)
8|Active | 1|gemini-blueprint-io (1.0.0.RELEASE)
9|Active | 1|Apache Felix Bundle Repository (1.6.6)
10|Active | 1|Apache Felix Gogo Command (0.12.0)
11|Active | 1|Apache Felix Gogo Runtime (0.10.0)
12|Active | 1|Apache Felix Gogo Shell (0.10.0)
13|Active | 1|Apache Felix Http Jetty (2.2.0)
14|Active | 1|Apache Felix Http Whiteboard (2.2.0)
15|Active | 1|Spring AOP (3.1.0.RC1)
16|Active | 1|Spring ASM (3.1.0.RC1)
17|Active | 1|Spring Aspects (3.1.0.RC1)
18|Active | 1|Spring Beans (3.1.0.RC1)
19|Active | 1|Spring Context (3.1.0.RC1)
20|Active | 1|Spring Context Support (3.1.0.RC1)
21|Active | 1|Spring Core (3.1.0.RC1)
22|Active | 1|Spring Expression Language (3.1.0.RC1)
Lyrical digression. The Jetty web server in delivery with felix 4.0.1 comes with version 6.1, which means you can forget about java-ee-6 with the annotation for the @WebServlet servlet. Jetty 8 did not have a desire to assemble separately, but it was not possible to launch it immediately.
So, to my surprise, after assembling and installing the package, I received a 404 error in response to Jetty and blamed everything on the lack of mapping.
Googling helped and the activator class was written to start the servlet:
public class Activator implements BundleActivator {
private ServiceRegistration reg;
@Override
public void start(BundleContext context) throws Exception {
Dictionary<String, String> props = new Hashtable<>();
props.put("alias", "/");
this.reg = context.registerService(Servlet.class.getName(), new TestServlet(), props);
}
@Override
public void stop(BundleContext context) throws Exception {
this.reg.unregister();
}
}
Project rebuilt, running.
The gender of the work is done, the servlet starts and responds, but the variable selializator disappointingly points to null.
It is worth noting that the @Component annotation on the servlet also had its effect. Looking ahead, I’ll say that two instances of the servlet were actually created. One was created by annotation and even contacted the serializer, and the second was honestly created in the activator and the @Autowired annotation did not work. The search for the service implementing ISerializer from the activator was not successful, because The activator call passed before the service I needed was registered in the system.
Ahead was waiting for another googling, which ultimately helped solve the problem.
If the class is described with the @Component annotation, then the @PostConstruct annotation can be added to one of the class methods (there are restrictions on the method, see the annotation documentation). In short, this method is called after all dependency injections.
So, we add the method that registers the servlet to Jetty.
@PostConstruct
public void postConstruct() {
BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
Dictionary props = new Properties();
props.put("alias", "/");
context.registerService(Servlet.class.getName(), this, props);
}
In tutorials on servlet-OSGi bundles, it is written that if you register a servlet yourself,
but it is necessary when unloading the module and re-register it yourself:
//
ServiceRegistration reg = context.registerService(Servlet.class.getName(), this, props);
//
reg.unregister();
In my case, the call unregister (tried to call from the destroy () method of the servlet) resulted in
error, which said that the service is no longer in that status, i.e. already unregistered. Apparently
@Component annotation takes care of this. At this I calmed down.
In the class-activator is no longer necessary. The result is a servlet into which you can inject
necessary implementation. And all this without tedious XML configuration files.
I hope that this article will help someone and get rid of the long search for information in the net.
Additional sources
Archive with an example