Microservices for Java EE/Jakarta EE Developers
Nowadays, the concept of microservices is more than a simple novelty. With the advent of DevOps and the boom of container technologies and deployment automation tools, microservices are changing the way developers structure their applications. With this article, our intention is to illustrate that microservices are a valid option for Java/Jakarta EE developers and how Payara Micro is a robust platform to reach that goal.
Advantages of Microservices
The main purpose of a microservice architecture is to break down an application into smaller standalone components that are easier to handle, deploy, scale and maintain in the long term. Sounds familiar? Yes, this is something that many developers have been doing since a long time ago! Encapsulation, cohesion and a good understanding of service-oriented architectures have helped them apply this “divide and conquer” strategy to software architecture for many years and will do so in the future as well.
So, what’s the advantage of a microservice architecture, then? Microservices are better understood when compared with their traditional counterparts: monolithic applications. Monolithic applications, or monoliths, are usually big enterprise applications structured into a single deployable package. If you want to introduce a new component or update an existing one, the entire monolith must be updated at once. This is the way enterprise applications were usually developed, focusing on the concept of tiers (UI, middleware-services, persistence, data) and the segregation of concerns across these tiers.
So, while a monolith is just a normal application developed with traditional means, the microservice architecture approach would be to break down such an application into its separate component units (or just services), so that each unit would fulfill the following criteria:
- Have a small footprint
- Be cohesive, e.g. focus on a single feature or business case
- Expose an interface to be used by other services of the same architecture or other external services
- Be independently managed, so that it can be coded, tested and deployed faster
- Be responsible for handling its own data
- Be isolated from other services, so no direct dependencies are needed
Monoliths vs. Microservices
So, does that mean that microservices should be used instead of the traditional monolith in all cases? Not necessarily. Microservices not only define a new architectural style, but also require that development teams build applications in a different manner.
There are also some advantages and disadvantages regarding when to use a monolith and when to use microservices: (Considered advantages are green, disadvantages are red, orange is for variable benefits depending on the software requirements or the organization’s environment.)
Monoliths | Microservices |
Cost of changes across all tiers is high. A change of a single feature means a redeployment of the whole unit. | Changes are easier to implement and not costly as they are targeted to specific services only. |
All components belong to a single unit, there’s no communication overhead. | Remote calls are needed for communication between services, so this overhead factors in the performance of the overall application. |
The entire development team must be familiarized with the design and composition of the entire application. | Each service can be handled by a separate team, so this favors separation of concerns and responsibilities. |
Developed with a technological stack in mind, using one or two languages of choice. A main language/framework is chosen to govern the software architecture. | Each service can be developed using a different framework based on its requirements and needs. Standards are discarded in favor of improving each service with the right tools. |
Easier to deploy. | The complexity of deployment increases with the number of services and the communication routes between them. |
Clustering can be as easy or difficult depending on all the features implemented across the monolith. | Since each micro-service is small, clustering is generally easy, with scalability being a primary drive. |
Failure of a component can cause the entire application to fail and hinder the user experience. | Failure of a service will only bring that specific service down, leaving other services untouched. |
There’s a focus on tiers and integration across tiers. | There’s a focus on business needs and communication concerns between teams. |
Microservices can help an organization develop their applications in such structured way. But sometimes, when applications are not critical to their business, or their quality attributes (performance, availability, scalability, security, etc.) are not specifically demanding, the traditional monolithic approach is more than enough.
Microservices and Java EE
The Java EE 8 set of specifications allows the creation of monolithic applications with ease. The main benefit of being a Java EE developer is that you don’t have to worry about handling technical concerns like network handling, transaction management or a resource’s lifecycle when the specific container service does that for you. This simplifies the developers' work allowing them to focus on business concerns instead.
In the case of Java EE, a monolithic application’s example could be an e-commerce application with the following characteristics:
- Is structured as an EAR file with multiple modules (linked to the tiers of the monolith):
- An EJB-module that handles integration aspects (SOAP web services, Message handling, etc.)
- Another EJB-module with a common persistence layer to access data stores with traditional means: JPA, JDBC, JCA, etc.
- Multiple WAR modules that correspond to the web applications that will handle the user interfaces. Let’s say this application has 3 WAR modules: One for an administrative interface, another to be used by providers or sellers and another for the use of buyers.
- Since the application’s built with Java EE, most of the code is written in Java.
- For the WAR modules, the user interfaces would be probably coded using JSF or JSP in conjunction with JavaScript. This would ensure consistency since the entire application is structured with the same web technology.
Sounds complex? Yes. The previous scenario was a common occurrence in earlier Java EE days, but nowadays, with tools like Maven or OSGi, modularizing Java EE application has simplified the development of monoliths on Java EE, meaning that your entire application can be deployed in a single WAR file. However, if you would like to implement this e-commerce application as a set of microservices, a simple WAR will not be enough.
There are no technology restrictions that prevent you to create microservices using the Java EE APIs via an application server like GlassFish (and Payara Server by extension). However, there are some considerations to have in mind:
- Since each microservice must be a complete standalone deployable unit, this means that each service should be composed of an application deployed within its own Java EE server. So if, for example, an application is composed of 10 micro services, then you would need 10 separate installations of an application server to host each service.
- Most application servers aren’t exactly lightweight, considering their complexity and the features they offer; e.g. Payara Server’s 5 Full Profile currently weights around 140Mb.
- Although most application servers have greatly reduced their startup times, there’s some overhead since the application server needs to prepare and handle many components that won’t be needed for a specific service. For example, when booting a GlassFish domain, the server needs to initialize its messaging subsystem, and this is something most services won’t need unless it’s strictly necessary.
Addressing the concerns
Payara Micro was created with these concerns in mind: it’s relatively smaller in size, packaged as a JAR, and allows developers to easily run a microservice with a simple command:
java -jar payara-5.191.jar --deploy user-service-1.0.war --contextRoot /
Run this command for each service and you don’t have to worry about a long startup time for your service or having to repeat the installation of a server multiple times! Since Payara Micro is based on the Java EE Web Profile, this means that complex or legacy APIs won’t be available (like JMS, JCA, JAX-WS, EJB Remote, etc.)
One of the main advantages of using Payara Micro to provision a microservices architecture is its ability to automatically form a cluster with other Payara Micro instances that are living in the same network. Here’s a the most basic sample of launching a cluster of 2 instances:
> java -jar payara-micro-5.191.jar --autoBindHttp --clusterName user-service --name us-instance-1
> java -jar payara-micro-5.191.jar --autoBindHttp --clusterName user-service --name us-instance-2
Once a new instance joins the cluster, every instance will report the current status of the Domain Data Grid (the conceptual equivalent of a modern cluster of server instances), letting you know that the cluster is scaling:
[INFO] fish.payara.nucleus.cluster.PayaraCluster] [tid: _ThreadID=61 _ThreadName=hz._hzInstance_1_user-service.event-3] [timeMillis: 1553274856105] [levelValue: 800] [[
Data Grid Status
Payara Data Grid State: DG Version: 35 DG Name: user-service DG Size: 2
Instances: {
DataGrid: user-service Instance Group: MicroShoal Name: us-instance-1 Lite: false This: true UUID: 634c290e-d972-45f0-93c8-241deb279f0d Address: /192.168.1.70:6900
DataGrid: user-service Lite: false This: false UUID: e5718197-6c83-4225-8aac-9d7892ef5bec Address: /192.168.1.70:6901
}]]
This is in thanks to Hazelcast IMDG, which is an open source distributed In-Memory Data Grid solution for Java applications that powers Payara Micro’s (and Server) clustering mechanisms. By default, Payara Micro instances cluster with each other using the multicast protocol, but in network environments where multicast is not supported (like Docker or certain cloud providers like AWS) other network communication mechanisms are supported as well. Additionally, Payara Services Ltd is working hard at implementing native support for a multitude of topologies and cloud providers, so keep and eye on what’s to come!
Jakarta EE – Bringing Java EE to the Future
At Java One 2017, Oracle made the huge announcement to open source the Java EE technology in its entirety, surprising the Java community. The new home of the Java EE technology would be the Eclipse foundation under the new name Jakarta EE (chosen in a democratic vote by the community after the Apache Foundation kindly “donated” the name).
As of March 2019, the process of transferring the entire set of specifications, reference implementations and TCKs to the Eclipse Foundation is still underway but a major milestone was reached at the end of January: Eclipse GlassFish 5.1 was released to the public! This version of GlassFish is technically compatible with Java EE 8 and will pave the way for the first release of the Jakarta EE specification: Jakarta EE 8 later in the year. Once that milestone is reached, the community will move forward to a new version of the specification which will include a lot of new improvements the existing APIs. This blog post from Arjan Tijms introduces the most interesting ideas that at the moment are being implemented in the existing projects that have been transferred to the Eclipse Foundation.
Having said all of this, what are the advantages of Jakarta EE over Java EE for microservice architectures then? Well, the answer is simple: None, since even after Jakarta EE 8 is released it won’t be any different in the scope of its features from Java EE 8. However, now that the technology has been open sourced and will be moved forward by the concerted efforts of the community, the existing APIs can be evolved with microservices patterns in mind, which will lead to a greater simplification of the development and deployment needed for these architectures! Even better, new APIs can be implemented in order to bridge the gap in the current set of features (like an API to modularize deployments or a standard API to orchestrate deployment units in a distributed arrangement). There are high expectations on the future of the Platform now that Oracle is no longer calling the shots with a renewed focus on modernizing the technology.
Eclipse MicroProfile
Although Jakarta EE is looking up good for providing a complete technology for microservices based on Enterprise Java, it is not the only technology that compatible Java EE servers can use. The Eclipse MicroProfile API is a set of standardized APIs aligned with the Java EE 8 specification that vendors can implement in order to develop microservices.
The goal of MicroProfile is to provide components that build upon the core features of Java EE allowing a more intuitive development experience when implementing microservices, granting more flexible options and reduce risks or over-designing and re-inventing the same patterns.
The current version of Eclipse MicroProfile is v3.3 and it is composed of the following set of APIs:
- CDI 2.0: Used as the “glue” or core of the entire technology in order to allow developers create re-usable components and easily handle dependencies across any application.
- JAX-RS 2.1: The set of standard APIs for handling RESTful services on both the server and client sides on enterprise applications and services.
- JSON-B 1.0: A new API introduced in Java EE 8, this API allows automatic serialization and deserialization of Java POJOs from and to JSON payloads, which is a must have when handling REST service calls.
- JSON-P 1.1: Used to leverage JSON documents processing capabilities to REST applications.
- Eclipse MP Config 1.4: Leverage standard mechanisms to retrieve configuration properties from different (and customizable) sources. These properties are made available to other application components via dependency injection or programmatic lookup.
- Eclipse MP Fault Tolerance 2.1: Provides several facilities to separate business logic from its execution patterns via multiple aspects like timeout, retry and fallback methods, bulkhead and circuit breakers processors. These aspects are based on existing CDI features.
- Eclipse MP Health Check 2.2: Allows the application to expose its status to other services and systems in the infrastructure.
- Eclipse MP Rest Client 1.4: Provides an API that allows a type-safe programmatic approach to call RESTful services over HTTP. The artifacts on this API are based on existing JAX-RS 2.1 features.
- Eclipse MP JWT Authentication 1.1: This API provides role-based access control to REST endpoints using standardized OpenID Connect and JSON Web tokens.
- Eclipse MP Metrics 2.3: Provides a unified way for microservices and applications to export and generate real-time monitoring data. The default format generated by this API is based on the Prometheus’ server data syntax.
- Eclipse MP OpenAPI 1.1: Used for documenting REST endpoints based on the OpenAPI v3 specification.
- Eclipse MP OpenTracing 1.3: Defines an API that allows services to participate in a distributed tracing environment, allowing users to monitor the state of current transactions.
Both Payara Server and Micro are compatible and certified implementations of Eclipse MicroProfile (version 5.191 is compatible up to MicroProfile v2.1 however). This means that you can use all MicroProfile’s APIs in conjunction with other Java EE APIs, and this is not limited for the development of microservices only. If you want to develop a traditional monolith while benefiting of the features and facilities introduced by MicroProfile, then you can just do that! Flexibility is a key concern of the Payara Platform.
Good Practices and Recommendations
You still need to go the extra mile and guarantee that your services fulfill the criteria described previously to develop enterprise-ready microservices, since the standard practices for developing Java EE applications will not be enough. The following is a list of my personal good practices and recommendations that you should follow when building micro services with Java EE and Eclipse MicroProfile:
Packaging
- Package your applications as WAR projects. There’s no need to use the EAR packaging, since that would imply a composition of modules, and a microservice should be structured using the minimum unit of deployment.
- Payara Micro allows the creation of Uber JARs: You can use it to create an auto-launchable fat JAR that contains all its necessary dependencies. This can be useful on cloud-native or containerized environments, especially when setting a continuous deployment pipeline using DevOps tools (using Jenkins for example).
Service Communication
- Define all the boundaries of your services using HTTP and REST whenever possible. This will guarantee that you will be able to communicate with other services that compose your application (even if they’re not developed using Java, Java EE or MicroProfile). Each microservice should have REST endpoints defined with JAX-RS.
- Use the Eclipse MicroProfile Rest Client API to call other services so that you can remove a lot of the boilerplate that is used to setup each HTTP request (including other services that are part of your architecture). Even better, you can define the interfaces for both JAX-RS services and its consumers in one place and re-use them accordingly.
- Document all REST endpoints thoroughly so that the communication rules are clear from the start. Use the Eclipse MicroProfile OpenAPI, which will allow you to annotate your JAX-RS service definitions in order to generate an OpenAPI document automatically.
- Is possible to use the CDI event bus provided by Payara Micro to propagate remote events from one service to another. Asynchronous communication like this, which follows a reactive model improves the decoupling of your services from each other. However, keep in mind that this feature is proprietary only to Payara Server and Micro, so only micro services deployed on them can use it. Consider using this feature only for establishing communication between services that belong to the same application (since all of them share the same technology stack). If you need to communicate with external services, rely on the usual HTTP communication instead.
Components
- Use CDI beans to define your applications components. Since CDI is both the cornerstone component API for both Java EE and Eclipse MicroProfile, you’ll benefit from the integrations that are available for both. Use Enterprise JavaBeans (EJB) only when a specific feature you need to use is not available on the CDI API (like timers for example).
- The Eclipse MicroProfile Configuration API allows a way to define a unified mechanism to load configuration values from multiple sources. The default sources supported by the API are:
- The /META-INF/microprofile-config.properties file located in the deployment unit.
- System properties
- Environment Variables
In most cases these 3 sources are more than enough for production needs (environment variables are fairly used in conjunction with containers), but the API allows for customizable configuration sources as well (like reading from a database or a configuration server too).
- Depend on stateless components all time! Stateful components are not only more complex to scale, but they will also make it harder to transfer user data between a set of micro services:
- When using CDI beans, use only the @RequestScoped and @ApplicationScoped scopes.
- If using EJBs, depend on @Stateless and @Singleton session beans only.
- Don’t store data in HTTP session objects. By default, the HTTP session object is managed by a web application, so define short-lived sessions (around 5 minutes) even if you don’t use it at all.
- Consider using HTML5 powered user interfaces for your web applications. Frameworks like AngularJS and ReactJS are good tools to build rich web interfaces nowadays.
- If you are considering using JSF pages for the user interface, consider using stateless views (<f:view transient=”true”>).
- Both Payara Server and Payara Micro support the JCache API, which allows application to cache values in memory. Caching data in this manner can greatly improve the performance of any microservice when used correctly. The JCache API’s implementation in the Payara Platform is provided by Hazelcast, which allows for distributed caching in any cluster arrangement, which can greatly simplify failover mechanisms as well.
Security and Data Management
- For services that need data stores, consider using small-sized relational databases like H2 or SQLite, so you can leverage JPA with its object relational capabilities (JPA is part of Java EE but not part of Eclipse MicroProfile, so keep this in mind). Both Payara Server and Payara Micro 5 already include H2 as their default relational database for internal use, so you can benefit from this inclusion as well.
- If you’re in need of a faster or easier to scale database, switch to a NoSQL solution that suits your needs (like MongoDB or Couchbase). Don’t worry about native integration with Java EE or Eclipse MicroProfile since most NoSQL databases already provide easy-to-use Java interfaces and plenty of documentation about production usage.
- In order to secure each micro-service implementation, rely on JSON Web Tokens (JWT). Since JWT is a widely industry-accepted standard, you can expect that your services will be compatible with most client consumers out there, and the specification is pretty robust as well. You can use the Eclipse MicroProfile JWT Authentication API to allow your micro-services to be automatically secured using a JWT provider (like Auth0 for example), adding role-based authorization using JAAS annotations like @RolesAllowed, @PermitAll and @DenyAll.
Infrastructure
Always make use of Payara Micro’s auto-clustering capabilities, which will allow you to implement elastic deployments in your infrastructure.
Docker containers are a must! Configure your application’s deployment with a Docker image that can help you automate the deployment process of your microservices. Better yet, use the official Payara Micro image to create your own!
Lastly, consider using orchestration tools to organize clusters of your micro services like Apache Mesos, Consul, Google Kubernetes or Docker Swarm. Both Payara Server and Payara Micro support native Kubernetes clustering even, so if you use it for orchestrating a distributed infrastructure, you can quickly configure how each node/instance joins the cluster using the kubernetes discovery mode provided by the Domain Data Grid.
Start your Microservices Journey
Microservices are here to stay, so any enterprise organization should consider implementing a microservice architecture when there are enough benefits to reap. For this reason alone, Java EE cannot be left behind and must move on in order to help developers transition to this style, which is goal that is no longer a dream thanks to both Jakarta EE and MicroProfile. Payara Micro is the ideal place to start, especially if you’re already familiar with the development of Java EE applications.
Keep in mind that microservices are not a silver bullet, and this should be common knowledge: use the traditional monolithic approach whenever possible, and switch to microservices when you think that their benefits suit your needs and justify their use. This transition not only means having a good grasp of the technical challenges you will be facing, but also understanding that there can be organizational and business constraints to be considered as well.