Scalable Microservices Using Modern Java-Based Frameworks
Take a look at KumuluzEE, a Java-based framework specifically designed for developers tooling around with microservices.
Join the DZone community and get the full member experience.
Join For FreeThe term “microservices” refers to one of the recent software architecture design patterns mostly used by modern software projects such as cloud systems. The microservices design pattern concentrates on the idea of deconstructing large and complex system structure to smaller and simpler independent components. Each of these independent components has a defined role and a set of services to fulfill.
The components in microservice-based systems are considered to act independently regardless of residing in one place or being spread across different locations. Each service or, to be more specific, each microservice is expected to be a fully independent sub-system that is totally separated from other sub-systems. The system as a whole is considered to be a network of service interactions fulfilled by the cooperation of independent service endpoints.
Achieving such modularity in design and implementation offers numbers of advantageous. The most remarkable advantageous offered by such design pattern is “Scalability.” The scalability for individual components (microservices) up to the system as a whole. The resources could be assigned and tuned more precisely based on the need of each microservice. Moreover, more stability and durability at the service level could be achieved.
In respect to the discrete architecture of these sort of systems, whenever a failure happens in any component, the fault is isolated from other parts of the system. The system is considered to be more fault tolerant. The maintainability of the system becomes easier. For instance, if part of the system needs to be updated, there is no need to stop the whole service. The system continues to serve while the update is in progress. Last but not least, the system components can be deployed on different locations. Considering the decentralized approach, the system could benefit from a range of various capabilities that are achieved by following a decentralized system architecture similar to cloud systems architecture.
There are a number of challenges in achieving such modularity and independence in design and implementation of such a system. The first and foremost challenge is maintaining overall component coherence while keeping the decentralized paradigm intact. In other words, keep the components decoupled while having the system functioning as a whole and avoiding any data and resource redundancy or lost. The other challenge is maintaining resource management by considering that system components may reside on different locations. Other challenges include the management and security issues of these sort of disparate systems, but those will not be covered in this article.
In the real world, modern enterprise development platforms, such as Java Enterprise Edition, provide technologies and tools, helping developers to develop systems based on modern design patterns. The environment provided by Java EE is divided into logical domains called containers. Containers are the infrastructures supporting the modular design and implementation of software components. Each container provides various underlying services such as Transaction, Security, Connectivity, and Messaging to the components it hosts. Java EE standard containers are: Web Containers, EJB Containers, Applet Containers, and Application Client Containers, and each hosts specific type of application.
Java application servers are implementations of the Java EE environment. Each application server provides standard Java EE containers plus the services that are offered by each container. In other words, each Java EE application server provides an environment that hosts modular enterprise Java-based applications and provides underlying services and technologies that are required by each deployed application. Such environment helps developers focus on business-oriented problems rather than basic service implementations. Java EE provides technologies that help to maintain modularity in design and implementation of enterprise software components. On a bigger scale, application servers could form a cluster, which provides a seamless environment to host distributed enterprise systems.
Considering the features provided by Java EE and the challenges that have been discussed so far, it seems that the challenge regarding the modularity and the coherence of overall system could be achieved by a good design and a set of appropriate tools and technologies, such as Java EE.
The other challenge is resource management. Each microservice normally focuses on a single business process or service. Therefore, it consumes specific runtime resources. For instance, in a sample Java EE environment, the microservice that is responsible for the UI service only requires the web container and the corresponding services that the web container provides. In the real world, the Java-based application server that hosts the UI microservice loads additional containers and services that are not required by the UI microservice. Loading these additional containers costs additional resources, such as memory. Be advised that each microservice is considered to be deployed independent of other microservices, therefore it requires a separate runtime environment.
To tackle the resource waste challenge, there are a number of possible solutions. One solution could be tweaking the application server settings to load only the required containers. This solution requires enough understanding about the architecture, configuration, and runtime specifications of each application server implementation. Another solution, and the more practical one, is to forget about the application server tweak. The developer has to inspect the technologies (or specifications, in terms of the Java EE ecosystem) required by each microservice, finding the corresponding implementations, doing the configuration, and finally constructing the environment by self.
Following any of these solutions distracts the developers from business-oriented problems and engages them with implementation and configuration challenges. This is totally in contrast with the arguments we have made so far.
A couple of months ago, I read an article about a Java-based framework named KumuluzEE. KumuluzEE is “A lightweight framework for developing microservices using standard Java EE technologies and migrating existing Java EE applications to microservices,” as mentioned on its site.
By automating the deployment and configuration of Java EE-based applications and environments, KumuluzEE helps the developers concentrate on business-oriented problems rather than the implementation. Developers only choose what Java EE-based technologies are required, and KumuluzEE handles the rest.
Currently, the following components are supported by KumuluzEE framework as mentioned by https://github.com/tfaga/KumuluzEE:
- Servlet 3.1 (Jetty).
- WebSocket 1.1 (Jetty).
- JSP 2.3 (Jetty Apache Jasper).
- EL 3.0 (RI UEL).
- CDI 1.2 (RI Weld).
- JPA 2.1 (RI EclipseLink).
- JAX-RS 2.0 (RI Jersey).
- JSF 2.2 (RI Mojarra).
- Bean Validation 1.1 (RI Hibernate validator).
- JSON-P 1.0 (RI JSONP).
To demonstrate the KumuluzEE, a simple modular microservice-based project consists of a Repository (Data) module, a RESTful WebService module for CRUD operations, and a Web portal (UI) module that will be built using the KumuluzEE framework. These modules could be deployed on a single server or deployed across multiple servers. In the following sections, the main features of the framework will be introduced.
The main project consists of three modules:
- Repository module: The module responsible for the data model of the system. It uses JPA (Java Persistence API) technology to persist and retrieve data from the database. This module will be used by other modules.
- CRUD module: The module that provides a RESTful web service to any backend system or other modules to perform CRUD operations. This module mainly uses JAX-RS (Java API for RESTful Web Services) technology.
- Web portal module: The UI module of the project that is responsible for retrieving any required dataset and presenting the user with reports, charts, and etc. This module mainly uses JSF (Java Server Faces) technology.
This project will be built using maven. The main project, MainProject, is the container of all modules. For each module (microservice), a new maven project will be created. The MainProject’s POM is as follows:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.sam</groupId>
<artifactId>MainProject</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>WebPortal</module>
<module>WebService</module>
<module>Respository</module>
</modules>
<properties>
<kumuluzee.version>2.1.0-SNAPSHOT</kumuluzee.version>
</properties>
</project>
The <kumuluzee.version> element is used for dependency management and compatibility issues between modules.
Let’s begin by constructing the Repository module. This module will be used by other modules for data persistence operations. This module is not a standalone service, so the process of creating is pretty much like a normal maven-based project. This project uses Java DB. The Repository module has two dependencies — derbyclient and kumuluzee-jpa-eclipselink. Kumuluzee-jpa-eclipselink is the JPA implementation provided by KumuluzEE. The POM is as follows:
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.sam</groupId>
<artifactId>MainProject</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>Respository</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-jpa-eclipselink</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.10.1.1</version>
</dependency>
</dependencies>
</project>
The Repository module contains the Projects.java entity class that is the representation of the corresponding table in database and the persistence.xml file. The Project entity will be used to store and retrieve data about some projects in the database. Other modules use Repository module to perform CRUD operations.
The Project.java entity class:
@Entity
@Table(name = "PROJECTS")
public class Projects implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "ID")
private Integer id;
@Column(name = "NAME")
private String name;
@Column(name = "STATUS")
private Short status;
public Projects() {
}
public Projects(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getStatus() {
return status;
}
public void setStatus(Short status) {
this.status = status;
}
}
The persistence.xml file :
<persistence>
<persistence-unit name="projects" transaction-type="RESOURCE_LOCAL">
<class>com.sam.respository.Projects</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/test"/>
<property name="javax.persistence.jdbc.user" value="test"/>
<property name="javax.persistence.jdbc.password" value="test"/>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
<property name="javax.persistence.schema-generation.create-source" value="metadata"/>
<property name="javax.persistence.schema-generation.drop-source" value="metadata"/>
</properties>
</persistence-unit>
</persistence>
Put persistence.xml inside the “src/main/resources/META-INF” folder and the repository module is ready.
Now consider the Web portal module. This module uses the repository module to retrieve information about the available projects in the database and graphically presents them to users. Web portal module uses Java Server Faces technology.
As a Java EE developer, to identify the dependencies for any given module, one must have enough knowledge of the Java EE technologies (specifications) that are required by that module. For instance, for the UI module, the following dependencies are identified and have to be set in the POM file:
<dependencies>
<dependency>
<groupId>com.sam</groupId>
<artifactId>Respository</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-core</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-servlet-jetty</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-cdi-weld</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-jsp-jetty</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-el-uel</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-jsf-mojarra</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
</dependencies>
KumuluzEE supports Jetty as the preferred servlet implementation. Mojarra is also supported by KumuluzEE as the default JSF implementation. Other implementations will be available in the future releases. An important dependency is kumuluzee-core. This is the KumuluzEE core module — the heart of the framework. The core module is responsible for identifying module dependencies, performing relative configurations, bootstrapping, and finally deploying and running the module. This is the magic of KumuluzEE. It frees the developer from any container or technology related configurations and implementations.
In the Web portal module, the MBean.java class acts as the backing bean. MBean has access to the database by use of the persistence context (thanks to the Repository module) and backs a .xhtml page with an ArrayList filled with Projects objects.
@Model
public class MBean {
@PersistenceContext(unitName = "projects")
private EntityManager em;
private List<Projects> Projectz = new ArrayList<>();
public List<Projects> getProjectz() {
Projectz = em.createNamedQuery("Projects.findAll")
.getResultList();
return Projectz;
}
}
To represent all projects graphically, the index.xhtml file is needed. The projects will be displayed on the index.xhtml page as a data table by use of the JSF h:dataTable tag. The index.xhtml file must be copied into src/main/webapp folder.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Projects</title>
</h:head>
<h:body>
<h2>Projects Data Table</h2>
<h:form>
<h:dataTable value="#{mBean.projectz}" var="project">
<h:column>
<f:facet name="header">ID</f:facet>
#{project.id}
</h:column>
<h:column>
<f:facet name="header">Project name</f:facet>
#{project.name}
</h:column>
<h:column>
<f:facet name="header">Project Status</f:facet>
#{project.status}
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
To finalize the process, the web.xml and bean.xml files must be created and put into the src/main/webapp/WEB-INF and src/main/webapp/META-INF folders respectively.
Another important thing to add to the POM is the maven-dependency-plugin. This plugin copies all classes and dependencies in one place that will be used by the KumuluzEE core module to bootstrap the module.
Run the maven package command to pack the project and issue the following command to run the module.
java -cp target/classes;target/dependency/* com.kumuluz.ee.EeApplication
By issuing this command, KumuluzEE core will identify the dependencies, locate them in the class path, do the appropriate configurations, and bootstrap the module. The module runs on the port 8080 by default. To change the default port on windows, issue the set command first. By opening the http://127.0.0.1:8080 address in the browser, the following table will be displayed (considering that we have only two entries in the projects table):
Projects Data Table
ID Project name Project Status
1 SAM 1
2 TOM 1
This is a simple web page to show the UI capability of this module. In the future, more detailed and complex features can be built around this concept.
The next module is the CRUD module. This module, with the help of the Repository module, exposes a RESTful web service API providing the CRUD capabilities to other modules. The CRUD module could be used to perform administration tasks such as add/remove or enabling/disabling any existing projects.
This RESTful web service provides three methods:
- insertProject(Projects project): To insert new Projects into the database.
- getProject(Integer id): To fetch information about the existing projects defined in the database.
- changeStat(Integer id): To enable/disable existing projects ( Change Stat ).
The following dependencies are set for this module:
<dependencies>
<dependency>
<groupId>com.sam</groupId>
<artifactId>Respository</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-core</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-servlet-jetty</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-cdi-weld</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-jax-rs-jersey</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-jsp-jetty</artifactId>
<version>${kumuluzee.version}</version>
</dependency>
</dependencies>
The SamResource.java class is defined as follows:
@Path("/samresource")
@RequestScoped
public class SamResource {
@PersistenceContext(unitName = "projects")
private EntityManager entityManager;
@GET
@Path("project/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getProject(@PathParam("id") Integer id) {
Projects projects = entityManager.find(Projects.class, id);
if (null != projects) {
return Response.ok(projects).build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
@GET
@Path("project/changestat/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response changeStat(@PathParam("id") Integer id) {
Projects projects = entityManager.find(Projects.class, id);
if (null != projects) {
if (projects.getStatus() == 1) {
projects.setStatus(((short) 0));
entityManager.getTransaction().begin();
entityManager.merge(projects);
entityManager.getTransaction().commit();
} else {
projects.setStatus(((short) 1));
entityManager.getTransaction().begin();
entityManager.merge(projects);
entityManager.getTransaction().commit();
}
return Response.ok().build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response insertProject(Projects project) {
try {
entityManager.getTransaction().begin();
entityManager.persist(project);
entityManager.getTransaction().commit();
return Response.status(Response.Status.CREATED).entity(project).build();
} catch (Exception exception) {
System.out.println(exception.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
}
Again a bean.xml file needs to be copied in src/main/webapp/META-INF.
Running this service is pretty similar to the procedures for the UI module. The CRUD module could be run on the same machine as the UI module, or it could be deployed on a different machine. The point is, these two modules are totally separated from each other, having their own resources and runtime environments. Each of these modules could be maintained and updated independently. Stopping one doesn’t have any impact on the whole service. For instance, the users of the UI module could continue using the system while the web service module is undergoing an update and vice versa.
Conclusion
To wrap it up, KumuluzEE is a very good starting point to consider in the design and implementation of microservice-based systems. Other frameworks, such as WildFly Swarm, are also available, they are out of the scope of this article. This article introduced the basic concepts of microservice design patterns and the advantages and challenges in implementing such design patterns. We also covered possible technologies and frameworks used to design and implement microservices, complete with a simple example.
Opinions expressed by DZone contributors are their own.
Comments