We are implementing OSGI on the Karaf platform

    OSGI is not difficult

    I have met many times that OSGI is difficult. And moreover, he himself once had such an opinion. Year in 2009, to be exact. At that time, we collected projects using Maven Tycho, and deployed them to Equinox. And it really was more difficult than developing and assembling projects for JavaEE (at that moment the version of EJB 3 just appeared, which we switched to). Equinox was much less convenient than Weblogic, for example, and the benefits of OSGI were not obvious to me then.

    But then, after many years, I had to start a project at a new job, which was conceived on the basis of Apache Camel and Apache Karaf. It was not my idea, I had known Camel for a long time by that time, and decided to read about Karaf, even without an offer. I read it one evening, and realized - here it is, simple and ready-made, almost the same solution to some problems of a typical JavaEE, similar to which I once did on my knee using Weblogic WLST, Jython, and Maven Aether.

    So, let's say you decide to try OSGI on the Karaf platform. Where do we start?

    If you want a deeper understanding

    You can of course start by reading the documentation. And it’s possible with Habré — there were very good articles here, let’s say such a long time ago . But in general, karaf received so far undeservedly little attention. There were a couple more reviews this or this . It’s better to skip this mention of karaf. As they say, do not read Soviet newspapers for the night ... for they will tell you there that karaf is an OSGI framework - so you do not believe it. OSGI frameworks are Apache Felix or Eclipse Equinox, on the basis of which karaf just works. You can choose any of them.

    It should be noted that when Jboss Fuse, or Apache ServiceMix is ​​mentioned, it should be read as “Karaf, with preinstalled components”, i.e. in fact - the same thing, only collected by the vendor. I would not recommend starting with this in practice, but it is quite possible to read review articles about ServiceMix, for example.

    To begin with, I’ll try to determine here very briefly what OSGI is and what it can be used for.

    By and large, OSGI is a tool for creating Java applications from modules. A close analogue can be considered, for example, JavaEE, and to some extent OSGI containers can execute JavaEE modules (say, web applications in the form of War), and on the other hand, many JavaEE containers contain OSGI inside as a means of implementing modularity "for themselves ". That is, JavaEE and OSGI are things similar to compatibility, and successfully complementary.

    An important part of any modular system is the definition of the module itself. In the case of OSGI, the module is called a bundle, and it is a jar archive well-known to all developers with some additions (that is, it is very similar here, for example, to war or ear). By analogy with JavaEE, bundles can export and import services, which are essentially class methods (that is, a service is an interface, or all public methods of a class).

    The bundle metadata is familiar to everyone META-INF / MANIFEST.MF. The headers of the OSGI manifest do not intersect with the headers for the JRE, respectively, outside the OSGI container bundle is a regular jar. It is significant that among the metadata there are always:

    Bundle-SymbolicName: com.example.myosgi
    Bundle-Version: 1.0.0

    These are the “coordinates” of the bundle, and the fact that we can have two or more simultaneously installed and working versions of the same bundle in one container is important.

    By analogy with JavaEE, bundles have a life cycle that looks like this: imageIn addition to services, bundles can also import and export packages (packages, in the usual sense of the term for java). Exported packages are defined inside the bundle, and are made available to other components when the bundle is installed on the system. The imported ones are defined somewhere from outside, must be exported by someone, and provided to the bundle by the container before it can start working.

    Package imports may be declared optional, as well as service imports. And it’s quite significant that import and export contain an indication of the version (or range of versions).

    Differences from JavaEE

    Well, it’s good that they are alike - we understood. And how do they differ?

    In my opinion, the main difference is that OSGI gives us much more flexibility. Once the bundle is in STARTED state, the possibilities are limited only by your imagination. Let's say you can easily create threads (yes, yes, I know about ManagedExecutorService), connection pools to databases, etc. A container does not take control of all resources to the same extent as JavaEE.

    You can export new services in the process. Try say in JavaEE dynamically create a new servlet? And here it is quite possible, moreover, the karaf servlet container created on the basis of jetty will be immediately detected by your created servlet and it will be available to clients at a specific URL.

    Although this is a slight simplification, but if the JavaEE application in its classical form consists mainly of components:

    • passive, waiting for a call from the client
    • defined statically, that is, at the time of the deployment of the application.

    On the other hand, an OSGI-based application may contain:

    • active and passive scheduled components, performing polls, well, listening to a socket, etc.
    • services can be defined and published dynamically
    • You can subscribe to framework events, for example, listen to the registration of services, bundles, etc., receive links to other bundles and services, and do much more.

    Yes, on JavaEE, much of this is also partially possible (for example, through JNDI), but in the case of OSGI, in practice it is made easier. Although there are probably a few more risks here.

    Differences between karaf and pure OSGI

    In addition to the karaf framework, there are a lot of useful things. In essence, karaf is a tool for conveniently managing the OSGI framework - installing bundles (including groups) there, configuring them, monitoring, describing the role model and ensuring security, and the like.

    And let's practice already?

    Well then, let's start right away with the installation. There isn’t much to write here - go to karaf.apache.org, download the distribution package, unpack it. Versions of karaf differ in support of different OSGI specifications (4, 5 or 6), and Java versions. I do not recommend the 2.x family, but here are 3 (if you have Java 8, like mine), and 4 can be used, although today only the 4.x family is developing (current version 4.2.2, it supports OSGI 6 and Java up to 10).

    Karaf works fine under Windows and Linux, everything you need to create a service and autorun is available. Support for MacOS and many other types of Unix is ​​also declared.

    You can usually start karaf right away if you're on the Internet. If not, then it’s usually worth fixing the configuration file, indicating where you have maven repository (s). Usually it will be a corporate Nexus, or say Artifactory, whoever likes what. The karaf configuration is located in the etc folder of the distribution. The names of the configuration files are not very obvious, but in this case you need the org.ops4j.pax.url.mvn.cfg file. The format of this file is java properties.

    You can specify the repository (s) both in the configuration file itself, listing the list of URLs in the settings, or simply showing where your settings.xml lies. There, the karaf will take the location of your proxy, which is usually necessary to know on the intranet.

    Kafar needs several ports, these are HTTP, HTTPS (if the web is configured, by default not), SSH, RMI, JMX. If they are busy with you, or you want to run several copies on the same host, you will have to change them too. There are approximately five of these ports.

    Ports such as jmx and rmi - here: org.apache.karaf.management.cfg, ssh - org.apache.karaf.shell.cfg, to change the http / https ports, you will need to create (most likely not) the etc / file org.ops4j.pax.web.cfg, and write the value org.osgi.service.http.port = port you need in it.

    Then you can definitely start it, and as a rule, everything will start. For industrial use, obviously, you will have to make changes to the bin / setenv, or bin / setenv.bat file, for example, to allocate the required amount of memory, but first, to see, it is not necessary.

    You can start Karaf right away with the console, the karaf command, or you can run it in the background with the start server command, and then connect to it via SSH. This is a completely standard SSH, with support for SCP, and SFTP. You can execute commands, and copy files back and forth. It is possible to connect with any client, for example, my favorite tool is Far NetBox. Login is available by login and password, as well as by keys. In giblets jsch, with all that it implies.

    I recommend having an additional console window immediately to view the logs that are located in data / log / karaf.log (and other files are usually there, although this is customizable). Logs are useful to you, from short messages in the console, not everything is clear.

    I would advise installing the web immediately, and the hawtio web console. These two things will make it much easier for you to navigate what is happening in the container and to steer the process from there to a large extent (as a bonus, you will get jolokia and the ability to monitor via http). Installing hawtio is done by two commands from the karaf console ( as described here ), and alas, today the version of karaf 3.x is no longer supported (you will have to look for older versions of hawtio).

    Out of the box, https will not be immediately, for this you need to make some efforts such as generating certificates, etc. The implementation is based on jetty, so all these efforts are mostly done the same way.

    OK, it started, what's next?

    Actually, what did you expect? I said it will be ssh. Tab works, if that.

    It's time to install some application. An application for OSGI is either a bundle, or consists of several bundles. Karaf can deploy applications in several formats:

    • A jar bundle, with or without an OSGI manifest
    • xml containing Spring DM or Blueprint
    • xml containing the so-called feature, which is a collection of bundles, other features, and resources (configuration files)
    • .kar archive containing several features and a maven repository with dependencies
    • JavaEE applications (under some additional conditions), for example .war

    There are several ways to do this:

    • put the application in the deploy folder
    • install from the console with the install command
    • install feature with the command from the feature: install console
    • kar: install

    Well, in general, this is quite similar to what a typical JavaEE container can do, but it’s somewhat more convenient (I would say it’s much more convenient).

    Simple jar

    The easiest option is to install a regular jar. If you have it in the maven repository, then the command is enough to install:

    install mvn:groupId/artifactId/version

    At the same time, Karaf realizes that he has a regular jar in front of him, and processes it, creating a bundle wrapper on the fly, the so-called wrapper, generating a default manifest, with package imports and exports.

    The sense of installing just a jar is usually not much, since this bundle will be passive - it only exports classes that will be available to other bundles.

    This method is used to install components like Apache Commons Lang, for example:

    install mvn:org.apache.commons.lang3/commons-lang/3.8.1

    But it didn’t work :) Here are the correct coordinates:

    install mvn:org.apache.commons/commons-lang3/3.8.1

    Let's see what happened: list -u will show us the bundles and their sources:

    karaf@root()> list -u
    START LEVEL 100 , List Threshold: 50
    ID | State     | Lvl | Version | Name                | Update location
    87 | Installed |  80 | 3.8.1   | Apache Commons Lang | mvn:org.apache.commons/commons-lang3/3.8.1
    88 | Installed |  80 | 3.6.0   | Apache Commons Lang | mvn:org.apache.commons/commons-lang3/3.6

    As you can see, it is quite possible to install two versions of one component. Update location - this is where we got the bundle, and where it can be updated if necessary.

    Jar and Spring context

    If there is a Spring Context inside your jar, things get more interesting. Karaf Deployer automatically searches for xml contexts in the META-INF / spring folder, and creates them if all external bundles needed by the bundle have been successfully found.

    Thus, all services that were inside the contexts will already start. If you had Camel Spring there, for example, Camel routes will start too. This means that we say a REST service, or a service listening on a TCP port, you can already start. Of course, launching several services listening on one port will not work out that way.

    Just Spring XML context

    If you had, for example, JDBC DataSources definitions inside Spring Context, then you can install them separately in Karaf. Those. take an xml file containing only DataSource as, or any other set of components, you can put it in the deploy folder. Context will be launched in the standard way. The only problem is that DataSources created in this way will not be visible to other bundles. They need to be exported to OSGI as services. About this - a little later.

    Spring dm

    What is the difference between Spring DM (OSGI-enabled version) and the classic Spring? So in the classic case, all the beans in the context are created at the initialization stage of the context. New ones cannot appear, old ones will not go anywhere. In the case of OSGI, new bundles can be installed and old bundles removed. The environment is becoming more dynamic, you need to somehow respond.

    The response method is called services. A service is usually a certain interface, with its own methods, which is published by some bundle. A service has metadata that allows it to be searched for and distinguished from another service that implements a similar interface (obviously, from another DataSource). Metadata is a simple set of key-value properties.

    Since services can appear and disappear, those who need them can either subscribe to services at startup or listen to events to find out about their appearance or disappearance. At the Spring DM level, in XML, this is implemented as two elements, service and reference, whose basic purpose is quite simple: publish the existing bean from the context as a service, and subscribe to an external service by publishing it to the current spring context.

    Accordingly, when initializing such a bundle, the container will find the external services it needs for it, and publish the bundles implemented inside it, making them accessible from the outside. A bundle starts only after service links are resolved.

    In fact, everything is a little more complicated, because the bundle can use a list of similar services, and subscribe immediately to the list. Those. a service, in general, has a property such as cardinality, which takes the value 0..N. In this case, the subscription, where 0..1 is indicated, describes an optional service, and in this case the bundle starts successfully even if there is no such service in the system (and instead of a link to it, it will get a stub).

    I note that a service is just any interface (or you can publish just classes), so you can well publish java.util.Map with data as a service.

    Among other things, service allows you to specify metadata, and reference allows you to search for a service by this metadata.


    Blueprint is the newer Spring DM incarnation, which is a bit simpler. Namely, if in Spring you have custom XML elements, then they are not here, as unnecessary. Sometimes this still causes inconvenience, but frankly - infrequently. If you are not migrating a project from Spring, you can start right away with Blueprint.

    The essence here is the same - it's XML, which describes the components from which the bundle context is assembled. For those who know Spring, there is nothing unfamiliar at all.

    Here is an example of how to describe a DataSource and export it as a service:

    Well, we deployed this file to the deployment folder, and looked at the results of the list command. They saw that the bundle did not start - in the Indtalled status. We try start, and we get an error message.

    Now in the list of bundles in the status Failed. What's the matter? Obviously, he also needs dependencies, in this case, a Jar with Oracle JDBC classes, or more precisely, the oracle.jdbc.pool package.
    We find the necessary jar in the repository, or download from the Oracle site, and install, as described earlier. Our DataSource has started.

    How to use all this? The service link is called in the Blueprint reference (somewhere, in the context of another bundle):

    Then, this bean becomes, as usual, a dependency for other beans (in the camel-sql example):

    Jar and Activator

    The canonical way to initialize bundles is to use a class that implements the Activator interface. This is a typical life-cycle interface containing start and stop methods that pass context . Inside them, the bundle usually starts its threads, if necessary, starts listening to ports, subscribes to external services using the OSGI API, and so on. This is perhaps the most complex, most basic, and most flexible way. For three years I have never needed it.

    Settings and configuration

    It is clear that such a configuration of the DataSource, as shown in the example, few people need. Login, password, and more, everything is hardcoded inside XML. It is necessary to take these parameters out.

    The solution is quite simple, and similar to that used in the classic Spring: at a certain point in the context life cycle, property values ​​are substituted from various sources.

    On this we will end the first part. If there is interest in this topic, then to be continued. We will consider how to assemble applications from bundles, configure, monitor, and automatically deploy systems on this platform.

    Also popular now: