There is hardly a Java developer who does not know what the Spring Framework is. One of the basic technologies of this framework is the IoC container and AOP support. These technologies allow you to successfully break the application architecture into separate layers, both at the class level and at the object layout level at run time. It would seem that the application is perfectly structured into elements / layers, but in essence it remains monolithic. Capless at runtime! Only within the framework of this framework, there are no universal solutions to this problem. Slightly fewer Java developers have heard of OSGi. This is the specification of modular systems for the Java platform. Using a specific implementation of this specification as the basis of an application allows you to make it modular, both at runtime and at the physical (file) level. About the synergy of these technologies and will be discussed in this article.
Spring framework
I expect that the reader is familiar with this framework at least at the level of reading the first ten chapters from the official manual. It is not possible to describe all this in one article. Therefore, I refer all those who need it to the official leadership. Below is just a brief help on the main components of the Spring Framework.
- The IoC container with the implementation of the DI pattern is a mechanism that controls the creation, configuration, and binding of objects at run time, the basic integral part of the framework.
- AOP - support for aspect-based programming. The basic mechanism that many subsystems use.
- Integration with data sources, ORM support and transaction support - mechanisms for interacting with various data sources from XML to DBMS, integration with third-party frameworks in these areas, support for local and global transactions, translation of specific access hierarchies to access data sources in their own hierarchy, etc. .
- Web components - provide various mechanisms from the native implementation of the MVC pattern to integration with various Web-related frameworks (JSF, Struts, WS, etc.).
Many different subprojects from the SpringSource portfolio.
Modularity
Ideally, a truly modular program should consist of elements that have the following pronounced features: weak connectivity (interaction through a well-defined interface, easy replaceability, repeated use), encapsulation (the module is viewed from the outside as a black box with a specific interaction interface), dynamism ( ability to change multiple modules at run time). Java allows for weak connectivity and encapsulation, both at the class level and at the package level. Of course, there are ways to break encapsulation at the level of access modifiers (private, protected) using reflection (Reflection API) and thereby link your code with the internal mechanisms of third-party code, or use the mechanisms for changing the code of classes (javassist, cglib, changing the code of classes manually and .d.), but it is rather khaki. But what about the dynamism, that is, the ability to replace modules at run time. In this area things are worse. You can of course implement dynamic support by redefining class loaders (ClassLoader) with your implementations, but this is a very large amount of work that implementers of OSGi have already done for us. Any OSGi container already supports modularity at the core architecture and API level.
')
OSGi
Open Services Gateway initiative is a specification with many implementations. The main open source implementations are Apache Felix, Equinox and Knopflerfish. This specification describes a modular system that can dynamically bundle various modules (bundles). The composition of the modules may change at run time. The interaction between the modules is carried out using services that are registered in the Service Register. Modules have a life cycle that consists of several states (INSTALLED, RESOLVED, STARTING, ACTIVE, STOPPING, UNINSTALLED). The life cycle of the module is managed by the OSGi container.
Module state diagram
The OSGi-module (bundle) must have additional metadata in the META-INF / MANIFEST.MF file. Some of the main ones are presented below:
- Bundle-Name - readable module name;
- Bundle-Version is the version of the module in the format number [.number [.number [.line]]], the default version is 0.0.0;
Bundle-SymbolicName - the symbolic name of the module, together with the version serve as unique identifiers of the module; - Export-Package - a list of exported Java-packages of the module with possible additional directives;
- Import-Package - a list of imported Java packages used in the module with possible additional directives.
How did you get the opportunity to use Spring with OSGi
OSGi implementations allow you to create truly modular Java programs. The Spring Framework makes it possible to get rid of manual support for connections between objects through the use of an IoC container, extend the functionality of existing classes using AOP, and use any related technologies from the SpringSource portfolio. Combining these technologies should bring considerable benefits in software development. Not surprisingly, this thought came to mind of several people from Interface21 (later SpringSource) in 2006 and they were able to connect people from OSGi Alliance, BEA, Oracle, IBM to the development. As a result of joint efforts, the Spring dm product appeared under the SpringSource wing, later this product gave life to the Blueprint specification in the OSGi Service Platform Service Compendium. Its codebase was taken over by the Eclipse Foundation and was named Gemini Blueprint. SpringSource also released dm Server, now its codebase has become a project of Virgo in the Eclipse Foundation.
Gemini Blueprint Architecture (Spring DM)
So how does Gemini Blueprint allow you to use the Spring Framework in an OSGi container? To answer this question, you need to consider the main points of the Gemini Blueprint architecture.
The basic element of the architecture is extender (implementation of the OSGi pattern extender). The Extender monitors the launch events for new modules (bundle) in the OSGi service platform and checks whether this module supports the blueprint specification. If the module supports this specification, extender creates the Spring context of the module and initializes it. Thus, each Spring module has its own Spring context. In addition, the extender calls the code that is responsible for publishing Spring-beans as OSGi services and getting links to OSGi services, and so on.
Extender function diagram
All information on the basis of which the extender creates the context is taken from the configuration as xml files. Here it is necessary to emphasize the existence of two possible configuration options spring and blueprint. Both options are very similar, but if the first corresponds to the native Spring configuration with additional named spaces for configuring services, the second is clearly defined by the Blueprint OSGi specification of the Service Platform Service Compendium. In fact, there is a third option, backward compatible with Spring dm.
For a more detailed study of the architecture, see the links at the end of the article.
Gemini Blueprint and OSGi usage example
As an example of use, we will write a GUI application with two implementations of services and one client. This example will demonstrate how the use of Gemini Blueprint makes it easier to develop OSGi programs. First, we do not need to use the OSGi API. Secondly, the issue of dynamic services is solved independently by Gemini Blueprint, we only need to take care of the application logic. Third, we will use the Spring Framework to build the application.
The application is built using maven 3. The file structure of the project is as follows:
blueprint example consumerββ consumer β βββ pom.xml β βββ src β ββββ main β β βββ java β β βββ blueprint β ββββ β β β ββββ ConsumerFrame.java Consumer.java resh β β βββ Consumer.java RefreshListener.java spring β resourcesββresource MET β β resourcesβββResa Spring β MET Osββ osgi-context.xml spring β βββ spring-context.xml test βββ test β βββ java β ββββ β date-producer β βββ pom.xml β βββ src main βββ main β β βββ java blue β β βββ blueprint example β β βββexample example β β ββββ dateproducer METββ META-INF spring β βββ spring β β ββ osgi-context.xml β β βββ spring-context. xml test βββ test β βββ java β βββ resources βββ int-producer β βββ pom.xml β βββ src main ββββ main β β βββ java β β β ββ β blueprint β β β βββ example int β β βββ intproducer resources β β β βββ ProducerImpl.java β β ββββ resources β β ββββ- osgi-context.xml spring β βββ spring-context.xml test βββ test β βββ java β βββ resources βββ pom.xml producerββ producer-api βββ pom.xml ββ Src mainββ main β βββ java blue β βββblueprint producer β βββ example β β ββββ producer β βββ api β β ββββ Producer.java βββ resources β β ββ META-INF βββ test βββ java ββ resources
In the consumer, producer-api, date-producer and int-producer directories, the client projects, the service API, the implementation of services based on date and integer are respectively located. Sounds confusing? Do not worry, now we will sort everything out in order.
Each of the listed projects is a separate module (bundle) for the OSGi-container. The project producer-api contains only one file with the Producer interface. This is a typical example of allocating an OSGi application API to a separate module that all modules that need it will import. This module is not a blueprint module, because its purpose is only to provide the necessary classes.
To build all the modules used maven-bundle-plugin. This is a plug-in from the Apache Felix developers, which uses the
Bnd utility (famous in OSGi circles). This plugin is configured as follows:
<plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.3.5</version> <extensions>true</extensions> <configuration> <instructions> <Bundle-Name> ${name} ${version} </Bundle-Name> <Bundle-SymbolicName> ${groupId}.${artifactId} </Bundle-SymbolicName> <Export-Package> blueprint.example.producer.api </Export-Package> <Import-Package>*</Import-Package> </instructions> </configuration> </plugin>
The attentive reader should note that the names of the entries in the instructions section match the names of the specific headers for the OSGi-module metadata in META-INF / MANIFEST.MF. This is what they are, not only in their natural form, but in the form of bnd instructions. This utility is quite intelligent and can calculate dependencies by code and substitute them into the Import-Package section, as well as add various metadata. Based on its configuration and calculations, it generates a MANIFEST.MF. In this case, we define only the main headers.
In order for the extender to recognize the module as a blueprint module, by default, the following rule is used: the module must have xml configuration files in the META-INF / spring directory, or the Spring-Context header indicating the location of the configuration must be present. The projects listed above use two configuration files in the META-INF / spring directory. The first with the prefix "osgi-" contains a blueprint-specific configuration. The second one with the prefix "spring-" contains the usual configuration for the spring-application.
Consider the implementation of services based on date and on the basis of an arbitrary integer:
@Component public class ProducerImpl implements Producer { @Override public String produceString() { return new Date().toString(); } } @Component public class ProducerImpl implements Producer { private Random random = new Random(new Date().getTime()); @Override public String produceString() { return String.valueOf(random.nextInt()); } }
The service in our training example is just a class with one method String produceString (), which returns an arbitrary string. In the first case, the localized date, in the second line with an arbitrary integer. In the spring configuration of these modules, the process of scanning annotated classes is specified. The osgi configuration registers the service.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd "> <context:annotation-config/> <context:component-scan base-package="blueprint.example"/> </beans> <?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.eclipse.org/gemini/blueprint/schema/blueprint" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation=" http://www.eclipse.org/gemini/blueprint/schema/blueprint http://www.eclipse.org/gemini/blueprint/schema/blueprint/gemini-blueprint.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd "> <service id="producerService" ref="producerImpl" interface="blueprint.example.producer.api.Producer"/> </beans:beans>
Here is the promised example of simplifying the life of a developer. There is no need to use the OSGi API to register the service, you just need to fill in the xml configuration.
It is time to consider the last module. The client of our services displays a GUI form with a text display field and a value update button. Let's take a look at the client code.
@Component public class Consumer { @Autowired private ConsumerFrame consumerFrame; @Autowired private Producer producer; @PostConstruct public void start() { consumerFrame.setRefreshListener(new RefreshListener() { @Override public String refresh() { return producer.produceString(); } }); SwingUtilities.invokeLater( new Runnable() { @Override public void run() { consumerFrame.setVisible(true); } } ); } }
Consumer class is very simple. It contains two attributes that are injected by the container. This is a form object and a link to our service. Further, in the method that is called after the construction of the object, a listener of the update event is registered with the service access code and the form display code on the screen. The spring configuration of this project looks identical to the one presented above, and the osgi configuration looks like this:
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.eclipse.org/gemini/blueprint/schema/blueprint" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation=" http://www.eclipse.org/gemini/blueprint/schema/blueprint http://www.eclipse.org/gemini/blueprint/schema/blueprint/gemini-blueprint.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd "> <reference id="producer" interface="blueprint.example.producer.api.Producer"/> </beans:beans>
In this configuration, a link to the service is registered. By default, this service is mandatory, and all calls to it, in its absence, will be blocked for a certain interval, after which an exception will occur. This behavior is completely configurable.
Programmers familiar with the Spring Framework probably had a question. How does the link to the service ensure the dynamism of this service? After all, all objects are configured at the download stage (eager), or at the appeal stage (lazy). What will happen if the producer link is no longer relevant due to the disappearance of the service. Answer: blueprint takes care of this. In fact, a proxy object is injected into the service as a reference to the service, which serves the dynamism. This object provides call blocking behavior as well as generating an exception.
In the archive with the source code, in addition to it there is a directory with a configured equinox container and all the necessary libraries (equinox-for-example). I think this will facilitate a quick start. To start the container, you need to go to the directory with it and execute the command:
java -jar org.eclipse.osgi_3.6.2.R36x_v20110210.jar -console
After it, messages from initializing modules will appear on the screen. And finally, the equinox command line prompt. After entering the ss command, the output should be identical to the one shown below:
osgi> ss
Framework is launched.
id state bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
1 ACTIVE com.springsource.net.sf.cglib_2.2.0
2 ACTIVE com.springsource.org.aopalliance_1.0.0
3 ACTIVE com.springsource.org.apache.log4j_1.2.16
4 ACTIVE com.springsource.slf4j.api_1.6.1
Fragments = 5
5 RESOLVED com.springsource.slf4j.log4j_1.6.1
Master = 4
6 ACTIVE com.springsource.slf4j.org.apache.commons.logging_1.6.1
7 ACTIVE org.eclipse.gemini.blueprint.core_1.0.0.RELEASE
8 ACTIVE org.eclipse.gemini.blueprint.extender_1.0.0.RELEASE
9 ACTIVE org.eclipse.gemini.blueprint.io_1.0.0.RELEASE
10 ACTIVE org.springframework.aop_3.0.6.RELEASE
11 ACTIVE org.springframework.asm_3.0.6.RELEASE
12 ACTIVE org.springframework.aspects_3.0.6.RELEASE
13 ACTIVE org.springframework.beans_3.0.6.RELEASE
14 ACTIVE org.springframework.context_3.0.6.RELEASE
15 ACTIVE org.springframework.context.support_3.0.6.RELEASE
16 ACTIVE org.springframework.core_3.0.6.RELEASE
17 ACTIVE org.springframework.expression_3.0.6.RELEASE
This is a list of all the modules currently installed. Now you need to install a test application. To do this, call the install file command: /// path_to_jar_module_file for each of the 4 modules (you can assemble the modules with the maven-package command). After that, run the start id command on the modules with the API, one of the services and the client. The test application window should appear on the screen.
Using date-producer
Then stop the service module with the stop id command and start another service. Refresh the window text field by clicking the refresh button.
Using int-producer
This completes the test case description. I want to emphasize that this is only a small part of the capabilities of Gemini Blueprint.
Application area
OSGi has long been something more than a standard for modular systems for embedded technology. In confirmation of the above, it is enough to give an example of such widespread OSGi-based projects like Eclipse and Glassfish. The Spring Framework has a strong position in the corporate market. What can the synergy of these technologies be used for? The main area of ββapplication of this bundle, according to the creators, was to be the area of ββcorporate applications. Actually this is a successful attempt to bring OSGi into the corporate world. And now we have the opportunity to use OSGi in any corporate and application applications with the Spring Framework.
disadvantages
Despite the great advantages that a dynamic modular architecture offers, the bundle of Spring and OSGi has its drawbacks. I think the most significant drawback is the relatively low prevalence of this ligament. Of course, separately, these technologies do not cause doubts in their survivability, but the number of users using them together is not so great. Subjectively, the process of transferring projects from SpringSource under the wing of the Eclipse Foundation has slowed their development. The second drawback is the additional complexity that the use of this bundle adds to the project. And this is not so much the complexity of using these technologies, as the complexity added by compatibility problems with third-party libraries, or rather the problems of using them in an OSGi container.
Projects associated with the use of Spring in the OSGi container
- Spring DM - a project designed to facilitate the use of Spring-applications in the OSGi-container. Currently not developing (see Eclipse Gemini).
- Spring DM Server is a fully modular Java application server designed for running enterprise applications and Spring applications. It has a high degree of flexibility and reliability. Currently not developing (see Eclipse Virgo).
- Gemini Blueprint - heir to Spring DM. The project allows you to run Spring-applications in the OSGi-container.
- Gemini Web is based on the reference implementation of the Web Applications specification from the OSGi Alliance. A project for running Spring Web modules in an OSGi container.
- Gemini JPA is a modular implementation of the Java Persistence API for an OSGi container. At the moment, it provides integration with JPA provider EclipseLink.
- Gemini DBAccess - provides distribution of JDBC drivers suitable for running in an OSGi container and mechanisms for using them.
- Gemini Management is a project for remote control of a modular system using JMX tools.
- Gemini Naming - JNDI support in OSGi-container.
- Eclipse Virgo - OSGi application server, inherited from Spring DM Server. Supports the following types of deployment formats (deployment formats): OSGi modules (bundle), Java EE WAR, Web modules (Web bundles), PAR (similar to EAR for Java EE, contains component archives), Plan (configuration of several modules into a common application ), Configuration (mechanism for dynamic updating of the application configuration). It contains a mechanism for hot deployment of applications, administration panel, additional libraries. It can be delivered with two Tomcat and Jetty servlet-containers. And also a lot of additional functionality.
Conclusion
I conceived the article as an introductory acquaintance with the technology of sharing Spring and OSGi. In order to fully describe all the information on the introductory part, I would need a much larger volume, so I suggest that the interested reader independently explore additional sources. In addition, I will be happy to answer questions on this wonderful technology (within the limits of my competence).
Additional sources
Archive with an exampleEclipse geminiBndOSGi SpecificationsSpring frameworkSpring dynamic modulesOSGi SpringSource BlogSpring Dynamic Modules in ActionEclipse virgo