How To Build Microservices With Java EE And JBossForge

By: Thorben
  |  March 11, 2024
How To Build Microservices With Java EE And JBossForge

Microservices have become one of the most popular architectural styles in the recent years. Previously, most development teams created huge monoliths which implemented all customer requirements. The downside of this approach is that the application often becomes a huge, unstructured combination of components, classes, and interfaces. That makes it hard to understand the overall system and to predict the side-effects of any changes to the existing code. It also makes it difficult to scale the application to fulfill new performance requirements.

These are just some of the reasons why microservices have become so popular. The general idea of this architectural style is to split the application into a system of independent microservices. Each service serves exactly one purpose and is a lot easier to understand and maintain as a huge monolith. You can even replace a microservice if your customer decides to change the requirements.

You can also scale your application more efficiently. Instead of scaling the whole application, you just add another instance of the service that needs to handle a high workload.

But this approach also introduces new challenges. Managing and monitoring a system of microservices takes a lot more effort and requires better tools than doing the same task for just one application. The distributed nature of this approach also requires you to handle latency and to implement your services as fault tolerant as possible.

In this first post of the series, I will show you how you can create a simple microservice with Java EE. Then we will add more services in the following posts and let them interact with each other to perform more complex tasks.

Misconceptions about Java EE

But before we start implementing a microservice with Java EE, we need a to talk about a few misconceptions. The most popular ones are the size of the application server and missing features in Java EE.

And yes, I wrote misconceptions because I think that you can build good microservices with Java EE. But don’t get me wrong. As with every technology, you will face challenges when you use Java EE. And there are good alternatives which you could use instead, like Spring Boot. But if you’re already familiar with Java EE, I suggest you give it a try.

So, let’s talk about the two most popular misconceptions.

How Big Is Too Big?

Some people say that a Java EE application server is too big to use it for microservices. But what does that mean?

What is too big? How many MBs of RAM are too many?

Everyone answers these questions differently. But most developers agree that the 30-45 MB RAM, required by a modern Java EE application server, are more than good enough for most use cases.

Sure, you can use different stacks that need a few MB less. But most companies don’t run enough microservice instances in parallel that saving 10 MB RAM per instance makes any difference. And if you think that your server should be as small as possible, you should have a look at distributions that are optimized for microservices, like Payara Micro or Wildfly Swarm.

So, when the application server doesn’t require too much RAM, the benefits it can provide become more important.

Benefits And Missing Features

OK, I could talk about this for ages. When you read this far, you’re probably familiar with Java EE, and you know that the different specifications define a lot of features that help you to implement your application.

And to make it even better, they were used to implement complex applications and have a proven track record. So, you don’t have to worry that you build your services based on an unmatured technology that might disappear or undergo incompatible changes in the future.

But are all these Java EE specifications a good fit for microservices?

That’s not an easy question, and my answer is: yes and no.

Reliable Implementations And Existing Expertise

Features like dependency injection with CDI, RESTful web services with JAX-RS and the ORM defined by JPA to store and retrieve data from a relational database are definitely useful when you want to build a microservice.

You can also work with all the tools you’re currently using. They were built to increase your productivity while implementing a traditional Java EE monolith and they work just as well when you build a microservice. One of these tools is JBoss Forge which I will show you later.

New Challenges Require New Tools

But building and operating a system of microservices takes more than that. You need to control latency, implement your services as fault tolerant as possible and monitor them in production.

Unfortunately, Java EE doesn’t provide a lot of support for that. But you can use external tools and libraries to extend the capabilities of the standard. You can use Retrace to monitor your applications and libraries like Netflix’s Hystrix to improve latency control and fault tolerance of your microservice.

So, as I said at the beginning, Java EE doesn’t provide everything that you need. But there are a lot of existing libraries that you can use in addition to Java EE.

What Is JBoss Forge?

JBoss Forge is a tool that I used a lot in the past. It’s pretty handy to get a project started or to create the first prototype that you can present to your customers.

It improves your productivity by generating boiler plate code and build configuration files for Java EE applications. I often use it to setup the project and to create the basic parts of the API and persistence layer. After that is done, you can focus on the important and more complex tasks, like the business logic, error handling, and validation.

What I especially like about it is that it doesn’t require you to use a specific structure and it also doesn’t tell you where you’re allowed to add your own code. If you use JBoss Forge to create your application, it will generate a standard build file and Java classes that you can edit as you like. And if you use it to adapt an existing project, it parses the existing files and makes the required adaptions.

You can use JBoss Forge either from the command line, which is what I will do in this post, or integrated into your IDE. Both approaches work fine. But it feels like; I’m a little bit faster with the command line interface. And I’m happy to take that extra bit of productivity.

But enough talking. There is no better way to show a tool than to use it. So, let’s build a small microservice with Java EE and JBoss Forge.

And don’t stop reading, if you’re not interested in JBoss Forge. It’s not mandatory to use it. It’s just a tool that I will use to make my work easier. If you prefer to type everything yourself, you can, of course, do that.

Implement Your Microservice

OK, so let’s start a new microservice for a book store. The project-new command generates a typical Maven project with the given name. When you use the default settings, the Maven build will package the project as a war file.

You can, of course, do the same with your preferred IDE or Maven archetype.

[Stackify-JavaEeMicroservices]$ project-new --named bookStore --stack JAVA_EE_7
***SUCCESS*** Project named 'bookStore' has been created.
***SUCCESS*** Stack 'Java EE 7' installed in project

Create a Simple Domain Model

Let’s continue with the domain model. A book store, obviously, needs to handle books. So, we need a book entity, and I want to persist the title and the publishing date for each book.

With JBossForge, you can create a new entity with the jpa-new-entity command. JBossForge automatically checks the dependencies of your project and adds missing ones.

[bookStore]$ jpa-new-entity --named Book
***SUCCESS*** Persistence (JPA) is installed.
***SUCCESS*** JPA Entity org.bookStore.model.Book was created
[Book.java]$ jpa-new-field --named title
***SUCCESS*** Field title created
[Book.java]$ jpa-new-field --named publishingDate --type java.time.LocalDate
***SUCCESS*** Field publishingDate created

You can see the created Book entity with its two attributes title and publishingDate below. Please be aware that JPA 2.1 was released before Java 8 and doesn’t support LocalDate as an attribute type. You will be able to use the classes of the Date and Time API with JPA 2.2. Until then, you need to rely on proprietary features of your persistence provider or implement an AttributeConverter that maps the LocalDate to a java.sql.Date. I’m using Hibernate 5 in this example which supports LocalDate as a basic type.

@Entity
public class Book implements Serializable {
    private static final long serialVersionUID = 1L;
	
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
    
    @Version
    @Column(name = "version")
    private int version;
    @Column
    private String title;
    @Column
    private LocalDate publishingDate;
	
    ...
}

Create a RESTful Webservice

The next thing you need for your microservice is an API. So, let’s create a RESTful web service to create, read, update and delete books.

The JBossForge command rest-generate-endpoints-from-entities adds the required dependencies to the project and generates a REST endpoint for the entity.

[Book.java]$ rest-generate-endpoints-from-entities --targets org.bookStore.model.Book --content-type application/json
***SUCCESS*** JAX-RS has been installed.
***SUCCESS*** Endpoint created

When you execute this command for the first time in your project, JBossForge creates a RestApplication class and the REST endpoint. You can see the endpoint in the following code snippet. JBossForge generated a stateless session bean with methods for the basic CRUD operations and annotated them with the required JAX-RS annotations.

@Stateless
@Path("/books")
public class BookEndpoint {
    
    @PersistenceContext(unitName = "bookStore-persistence-unit")
    private EntityManager em;
    @POST
    @Consumes("application/json")
    public Response create(Book entity) {
        em.persist(entity);
        return Response.created(
                UriBuilder.fromResource(BookEndpoint.class)
                        .path(String.valueOf(entity.getId())).build()).build();
    }
    @DELETE
    @Path("/{id:[0-9][0-9]*}")
    public Response deleteById(@PathParam("id") Long id) {
        Book entity = em.find(Book.class, id);
        if (entity == null) {
            return Response.status(Status.NOT_FOUND).build();
        }
        em.remove(entity);
        return Response.noContent().build();
    }
    @GET
    @Path("/{id:[0-9][0-9]*}")
    @Produces("application/json")
    public Response findById(@PathParam("id") Long id) {
        TypedQuery findByIdQuery = em
                .createQuery(
                        "SELECT DISTINCT b FROM Book b WHERE b.id = :entityId ORDER BY b.id",
                        Book.class);
        findByIdQuery.setParameter("entityId", id);
        Book entity;
        try {
            entity = findByIdQuery.getSingleResult();
        } catch (NoResultException nre) {
            entity = null;
        }
        if (entity == null) {
            return Response.status(Status.NOT_FOUND).build();
        }
        return Response.ok(entity).build();
    }
    @GET
    @Produces("application/json")
    public List listAll(@QueryParam("start") Integer startPosition,
            @QueryParam("max") Integer maxResult) {
        TypedQuery findAllQuery = em.createQuery(
                "SELECT DISTINCT b FROM Book b ORDER BY b.id", Book.class);
        if (startPosition != null) {
            findAllQuery.setFirstResult(startPosition);
        }
        if (maxResult != null) {
            findAllQuery.setMaxResults(maxResult);
        }
        final List results = findAllQuery.getResultList();
        return results;
    }
    @PUT
    @Path("/{id:[0-9][0-9]*}")
    @Consumes("application/json")
    public Response update(@PathParam("id") Long id, Book entity) {
        if (entity == null) {
            return Response.status(Status.BAD_REQUEST).build();
        }
        if (id == null) {
            return Response.status(Status.BAD_REQUEST).build();
        }
        if (!id.equals(entity.getId())) {
            return Response.status(Status.CONFLICT).entity(entity).build();
        }
        if (em.find(Book.class, id) == null) {
            return Response.status(Status.NOT_FOUND).build();
        }
        try {
            entity = em.merge(entity);
        } catch (OptimisticLockException e) {
            return Response.status(Response.Status.CONFLICT)
                    .entity(e.getEntity()).build();
        }
        return Response.noContent().build();
    }
}

Except for the missing LocalDate support in JPA 2.1, I used plain Java EE APIs to create this service. You can deploy this application to any Java EE 7 compatible application server that uses a persistence provider which supports the LocalDate as an attribute type.

Build, Deploy And Run Your Microservice

You can either execute the Maven build directly, or you can use the JBossForge command build, which triggers the Maven build for you.

[BookEndpoint.java]$ build

The build creates a bookStore.war file and puts it into the target folder. You can then deploy it into your Wildfly server by copying it to your_wildfly_directory/standalone/deployments or via the jboss-cli command line interface.

The only thing you now need to do is to execute the standalone.bat or standalone.sh file, and your Java EE service will be up and running.

What’s next?

In this blog post, we created the first microservice of our book store application. It’s just a small service that implements a set of basic CRUD operations. So, to be honest, there are still a lot of things missing.

The service doesn’t provide any business logic, and it doesn’t provide any monitoring information. You can use Retrace to monitor the service, but I still need to implement the business logic.

I will also need more than one service to implement a book store. That will be the main topic of the next post. I will create a service to handle the customer information and show you how to implement the interaction between two services.

Improve Your Code with Retrace APM

Stackify's APM tools are used by thousands of .NET, Java, PHP, Node.js, Python, & Ruby developers all over the world.
Explore Retrace's product features to learn more.

Learn More

Want to contribute to the Stackify blog?

If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]