That modularity is not supported in Java is well known to all. The problem has existed for a long time, and there have been many attempts to correct the situation. The Jigsaw project is dedicated to this goal. In 2011, it was planned to add support for modularity in Java 7, in 2014 - in Java 8, finally, it was announced that at the end of July 2017, Java 9 will be released with the possibility of modularization. But what to do in anticipation of this beautiful moment?
OSGi to the rescue
To create a truly modular architecture in the Java language, you can use the OSGi (Open Services Gateway Initiative), a specification of a dynamic modular system and service platform for Java applications (developed by OSGi Alliance). It describes a software development model in which components have three main features of a modular application. Speech is about encapsulation (each module hides its implementation from the external environment), weak connectivity (modules only interact with pre-specified contracts) and dynamism (components can be replaced on the fly, without stopping the entire application). The basis of the concept of OSGi are 2 entities: sets (Bundles) and services (Services).
Bundles
When developing software in Java, as a rule, third-party libraries are used. In the world of Java-libraries are packaged in files with the extension JAR (Java ARchive) - an ordinary ZIP-archive containing Java-classes (files with the extension .class). In this case, the use of libraries may be associated with certain difficulties.
As soon as you begin to use any library, all classes in it, as a rule, become available to you. The fact is that library developers are not able to hide classes that are used to implement their internal logic. Thus, if you use code that was not intended to be used outside the library, you may encounter incompatibilities when using the new version of the library or disrupt its proper functioning.
')
Another problem is the so-called JAR Hell, about which the developers broke a lot of copies. Its essence is as follows: as soon as you start using different versions of the same library (this happens in large projects that evolve over time), you may encounter the fact that the same class has different methods in different versions of the library. Java is designed in such a way that the first version of the library that the class loader finds will be used. Thereby, having addressed to a class in the code during the execution of the program, you will get an error that the method to which you refer does not exist. This is due to the fact that at runtime Java does not know anything about the version of the library that should be used in this or that case.
The OSGi developers did not begin to change the structure of JAR files to ensure modularity, but simply added additional information to them, which is used by the OSGi environment. Moreover, this information does not affect the use of JAR-files in conventional Java-applications. So, to make the JAR file an OSGi set, it adds data that defines the Export-Package (packages of this set available for use outside of it) and Import-Package (packages of other sets required for this set to work). It is possible to specify both the version of the API that the set provides for other sets, and the version or range of versions of the API that the set requires to work from them. All collection classes that are not in its exported section are not accessible outside the collection (Private). In this way, the OSGi set fulfills the requirement of weak connectivity.
Today, most Java libraries are already OSGi ready, i.e. contain information for being able to run in an OSGi container. In addition, there are many tools and utilities with which you can create modules for OSGi from ordinary JAR-files.
Fig. one.
In OSGi, you can easily avoid JAR Hell situations, since the dependencies between the sets and the interfaces provided by these sets have clear versions. You can dynamically load and unload new sets at runtime. OSGi tracks dependencies between sets and dynamically resolves them.
Services
So, with the help of OSGi-sets, we can develop modular applications that interact through interfaces (APIs). But where to get the class that implements the required interface? The option “add to the set API” is not suitable - within the framework of the modular architecture, we have agreed not to use the internal classes of the sets outside these sets. Another solution is to apply the “factory” template, implementing the interface, and add it to the set API. But it is not too successful, because To hide the interface implementation, you will have to develop a new class each time.
The search for the implementation of the interface in OSGi is performed using the service registry. In this registry, a set can register an implementation with an interface describing it. A set that uses an interface from another set can find in the registry the desired implementation of the interface it needs. As a rule, sets register services when launched in an OSGi container. Plus, the same interfaces with different implementations and additional identification data can be registered in the service registry. Using filtering, the set can select the most suitable of the presented services in the registry.
Fig. 2
Java Virtual Machine Microservices
Microservice architecture is a set of independent modules - individual applications. The interaction between them takes place through well-defined interfaces using lightweight protocols (REST, Protocol Buffers, MQ, etc.). In fact, each module is a microservice that performs one specific task and contains, as a rule, the minimum amount of code. The advantages of this software development approach are:
- ease (when developing microservice, only one part of the program’s functionality is implemented).
- ease of replacement (if the service does not cope with its task, it can be rewritten and replaced with a new version without stopping the current software).
- reuse (microservice can be reused where it suits).
Developers of modular applications using OSGi have long enjoyed all these advantages, but only within the framework of the Java virtual machine. Each set with OSGi that publishes a service to the registry is a microservice inside the Java Virtual Machine, JVM (in the OSGi world, such microservices are called µServices).
Red Hat JBoss Fuse
We at Jet use all the benefits of OSGi when developing software for telecom operators, insurance and processing companies. To do this, we use Red Hat JBoss Fuse (Fuse Fabric configuration). This platform provides a flexible OSGi environment for running a modular application.
Application continuity, easy horizontal scaling, ease of deployment, the availability of cluster management tools for cluster software - all of these features are available in Fuse Fabric. The technology allows you to deploy multiple instances of Red Hat JBoss Fuse and merge them into a cluster, and also provides a centralized tool for managing the resulting environment.
The following abstractions exist within Fuse Fabric:
Features (features) - a set of OSGi-sets that implement any functionality.
Profiles (profiles) - a set of features that must be performed within a single container, and configuration settings for sets that are included in the feature.
Containers (containers) are separate JVM processes that run on a specific node of a Fuse Fabric cluster under the control of a Red Hat JBoss Fuse container.
Fig. 3
Any software based on Fuse Fabric technology consists of OSGi-sets, which are grouped into features and deployed as part of a separate JVM process on one or several cluster nodes in accordance with a predefined profile.
Fuse Fabric provides many tools for easy management of the resulting cluster. You can create profiles (and based on them, containers), create / delete / start / stop containers on any host in the cluster, connect new cluster nodes, etc. And all this online - without interrupting the functioning of the other nodes of the cluster. It is possible to store multiple versions of profiles, features, OSGi-sets.
Thanks to the distributed OSGi technology, in the resulting environment, OSGi-sets can interact within the framework of one and different containers and even different hosts. Additionally (with minimal development costs) each service that an OSGi-set provides can be turned into a REST / web-service that can be called from external systems.
Thus, using Fuse Fabric, we can create a microservice architecture that supports ease of configuration and deployment, isolated execution of services within its JVM processes (with its specific settings, for example, different garbage collector settings), while the services interact with each other network using lightweight protocol.
Since Red Hat JBoss Fuse product is based on the Open Source technology stack Apache ServiceMix, we have at our disposal such powerful technologies as Apache ActiveMQ (implementation of the JMS specification), Apache Camel (implementation of enterprise application integration patterns), Apache CXF (library for developing REST / web -services), Blueprint (provides opportunities for dependency injection), Spring, etc. Since these technologies are seamlessly integrated with each other in Red Hat JBoss Fuse, this significantly reduces the development time of the required functionality.
The material was prepared by experts of the Jet Infosystems software solutions center.