DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • 7 Microservices Best Practices for Developers
  • Spring Reactive Microservices: A Showcase
  • Practical Transaction Handling in Microservice Architecture
  • Fixing Your Microservices Architecture Using Graph Analysis

Trending

  • Using Python Libraries in Java
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • The Smart Way to Talk to Your Database: Why Hybrid API + NL2SQL Wins
  • How To Build Resilient Microservices Using Circuit Breakers and Retries: A Developer’s Guide To Surviving
  1. DZone
  2. Data Engineering
  3. Databases
  4. Programming Styles Compared: Spring Framework vis-a-vis Eclipse MicroProfile Part 1

Programming Styles Compared: Spring Framework vis-a-vis Eclipse MicroProfile Part 1

A developer takes a comparative look into the Spring and MicroProfile frameworks for microservices development.

By 
Konur Unyelioglu user avatar
Konur Unyelioglu
·
Mar. 12, 19 · Tutorial
Likes (17)
Comment
Save
Tweet
Share
21.0K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

In this article series, we will compare two open source microservices frameworks, Spring and Eclipse MicroProfile, for developing microservices. A simple microservice application will be developed twice, based on the same class design, once via the Spring framework and once via the Eclipse MicroProfile with Open Liberty runtime. Those separate versions of the same application will be built with Maven to create individual Docker images. We will then execute and test each version separately. With this exercise, our goal is to highlight some of the similarities and differences between the two technologies in terms of basic configuration, object annotations, build/execution instructions, and commands to create Docker images.

Microservices is an architectural style to develop component-based software applications tailored around business capabilities. A microservice is a small unit of application code and related configuration that can be quickly developed, tested, and moved to production. Below are some of the basic features of microservices. (Also see this article and this knowledge resource.)

  • Microservices can be brought from concept to production quickly.

  • Microservices provide visibility and transparency to their intrinsic state so that their application state and health can be easily monitored via external tools, e.g. Spring Boot Actuator.

  • Microservices support configuration via an external, centralized configuration service, e.g. Spring Cloud Configuration Server.

  • The development and maintenance philosophy behind a microservices architecture promotes the ‘build-it/run-it’ DevOps paradigm.

  • Microservices can register themselves with a service registry, e.g. Netflix Eureka so that they can be searched and located by their clients via discovery services.

  • Multiple instances of a microservice can be deployed to support fault tolerance. A microservice architecture enables client-side load balancing, e.g. using Netflix Ribbon, for clients of a microservice to access multiple instances of the microservice in a load-balanced manner. A microservice itself could utilize client-side load balancing while accessing other services it has a dependency on.

  • In addition, microservice architectures support:

    • integration with cluster configuration and coordination services, e.g. Apache ZooKeeper, for managing microservices in a clustered environment.

    • synchronization of message distribution and processing to allow asynchronous communication between microservices, e.g. via Spring Integration.

    • commonly used authentication and authorization technologies such as OpenID, OAuth, and SAML.

Spring is a popular open source framework to develop web services using the Java language. It mainly consists of a core container system with various add-on modules that provide programming services such as security, data access, messaging, and transaction management, to name a few. The Spring framework can be effectively utilized to develop microservices using Spring Boot and Spring Cloud, two sub-projects of the Spring framework. (Although a little dated, this tutorial and this discussion are still relevant.) 

Eclipse MicroProfile is an Eclipse open-source project to define a programming model for developing microservice applications in an Enterprise Java environment. One of the main goals of the project is to ensure portability across different vendor runtimes of the applications built according to the Eclipse MicroProfile APIs. Another related focus of the project is to ensure the interoperability of those applications between different vendor runtimes. In this article, we will utilize IBM’s Open Liberty application server as the Eclipse MicroProfile runtime (see the project website for a list of vendor environments supporting Eclipse MicroProfile). 

This article series is organized as follows. The next section, 'Class and Data Models,' describes the design model for a simple application that will be developed separately using Spring and Eclipse MicroProfile frameworks. The following section, 'Code Review,' gives a review of the Java code and configuration files. Here, we also review the Docker files for assembling the respective Docker images. That section is followed by Part 2, 'Running the Application,' where we demonstrate how to run and test each application. We then end the series, and Part 2, with 'Conclusions.' 

The files discussed in this article can be downloaded from GitHub.

Class and Data Models

The Inventory Application is a web service with three operations regarding car inventories. 

  • View existing car inventory.

  • Create new car inventory.

  • Update existing car inventory.

The application has a simple class model represented by the following UML diagram.

Class Model

Figure. Class Model.

A rest package is the entry point for web service calls and consists of InventoryApplication, which performs basic application configuration, and InventoryResource, which generates the response for various REST (Representational State Transfer) calls.

The dao (data access objects) and dao.entities packages provide functionality for the data access layer. InventoryEntity is an object representation of an inventory record in a database table and InventoryRepository specifies methods to create, view, and update inventory records (an inventory record consists of a car brand and an integer indicating the inventory, e.g. {"brand":"BMW","inventory":"15"}).

InventoryService in the service package serves as an intermediary between the rest and dao packages and interfaces with InventoryRepository to create, view, and update inventory records according to the requests coming from its client, InventoryResource.

The model package consists of Inventory, that has similar attributes to InventoryEntity. However, while InventoryEntity has persistence awareness, InventoryEntity is a plain Java bean.

The rest package both accepts input data and generates output data in JSON (JavaScript Object Notation) format. Data input from the web service calls are mapped to Inventory objects in the rest package and they are converted to InventoryEntity objects in the service package while forwarding the requests to the dao package. Conversely, a response from the dao package in the form of InventoryEntity objects is transformed back to a response in the form of Inventory objects in the service package.

The simple pattern above can be seen as an implementation of the model and controller components in the Model-View-Controller architectural style. We omit the View component as we assume it can be implemented separately via JavaScript and Dynamic HTML for execution in a web browser.

Please note that the above design is not intended to prescribe a pattern to develop microservices. It is simply a baseline to code the same inventory application using two distinct frameworks and then compare them with each other. Also, for simplicity, the design does not take into account some other important aspects of microservices such as health checks, service registry, and fault tolerance.

Data Model

The data model is very simple; it consists of a relational database table called CAR with two columns:

  • brand VARCHAR(25) PRIMARY KEY

  • inventory INT

Technology Components

We will create two separate implementations of the design. In one, we will use the Spring framework. In the other one, we will use the Eclipse MicroProfile. The main components of the Spring application are as follows:

  • Spring Boot 2.1.2

  • Spring Cloud Greenwich

  • Spring Data JPA 2.1.4

  • Spring ORM 5.1.4

The main components of the Eclipse MicroProfile application are as follows:

  • Open Liberty MicroProfile API 2.1
  • JAX-RS 2.1

  • JSONP 1.1

  • CDI 2.0

  • JPA 2.2

  • Open Liberty Runtime 19.0.0.1

Both applications will use Derby Network Database version 10.14.2.0 with client version 10.10.1.1. Each application will be converted to a Docker image to be executed in Docker engine version 18.09.2 in MacOS environment. 

Code Review

Following the design model above, the folder structure for the Spring application is as follows.

- Dockerfile
- pom.xml
- src
-- main
--- java
------- org
--------- springexamples
-------------- inventories
------------------------ dao
-------------------------- InventoryRepository.java
-------------------------- entities
---------------------------- InventoryEntity.java
------------------------ model
-------------------------- Inventory.java
------------------------ rest
-------------------------- InventoryApplication.java
-------------------------- InventoryResource.java
------------------------ service
-------------------------- InventoryService.java
--- resources
------ inventory-application.yaml
------ logback.xml

Similarly, the folder structure for the Eclipse MicroProfile application is as follows:

- Dockerfile
- pom.xml
- src
-- main
--- java
------- org
--------- olpexamples
-------------- inventories
------------------------ dao
-------------------------- InventoryRepository.java
-------------------------- entities
---------------------------- InventoryEntity.java
------------------------ model
-------------------------- Inventory.java
------------------------ rest
-------------------------- InventoryApplication.java
-------------------------- InventoryResource.java
------------------------ service
-------------------------- InventoryService.java
--- resources
------ META-INF
-------- beans.xml
-------- persistence.xml
--- webapp
------ WEB-INF
-------- web.xml
--- liberty
------ config
-------- server.xml

For both Spring and Eclipse MicroProfile applications, the package structure has been defined with the following in mind.

  • The dao package represents the database access layer.

  • The model package encapsulates beans that represent conceptual models of the application.

  • The service package encapsulates objects that perform service operations. This layer has the awareness of both the dao and model layers.

  • The rest package encapsulates the application entry point and the controller object that translates REST calls to commands to be performed by the service. 

Java Code

InventoryEntity.java

This file represents an entity class corresponding to the CAR table. The code for Spring and Eclipse MicroProfile are very similar except that in the case of Eclipse MicroProfile we explicitly define the named queries findAlland  findById. In the case of Spring, we will rely on the Spring Data JPA framework for the implicit definition of those queries.

Spring

package org.springexamples.inventories.dao.entities;
import java.io.Serializable;
import javax.persistence.*;

@Entity@Table(name = "car")
public class InventoryEntity implements Serializable {    
  private static final long serialVersionUID = 1L;    

  @Id    
  @Column(nullable = false, length = 25, name = "brand")    
  protected String brand;    

  @Column(nullable = false, name = "inventory")    
  protected Integer inventory;    

  public String getBrand() {        
    return brand;    
  }    

  public void setBrand(String brand) {        
    this.brand = brand;    
  }    

  public Integer getInventory() {        
    return inventory;    
  }    

  public void setInventory(Integer inventory) {        
    this.inventory = inventory;    }
}

Eclipse MicroProfile

package org.olpexamples.inventories.dao.entities;
import java.io.Serializable;
import javax.persistence.*;

@Entity@Table(name = "car")
@NamedQueries(
  {@NamedQuery(name = "InventoryEntity.findAll", 
               query = "SELECT e FROM InventoryEntity e"),
   @NamedQuery(name = "InventoryEntity.findById", 
               query = "SELECT e FROM InventoryEntity e WHERE e.brand = :brand")}
)

public class InventoryEntity implements Serializable {    
  private static final long serialVersionUID = 1L;    
  @Id    
  @Column(nullable = false, length = 25, name = "brand")    
  protected String brand;    

  @Column(nullable = false, name = "inventory")    
  protected Integer inventory;    

  public String getBrand() {        
    return brand;    
  }    

  public void setBrand(String brand) {        
    this.brand = brand;    
  }    

  public Integer getInventory() {        
    return inventory;    
  }    

  public void setInventory(Integer inventory) {        
    this.inventory = inventory;    
  }
}
InventoryRepository.java

This class encapsulates operations against the CAR table, for finding all inventories, creating a new inventory, and updating an existing inventory.

In the case of our Spring application, this is an interface extending JpaRepository in the Spring Data JPA framework. Because the JpaRepository takes care of all the operations behind the scenes, we don't need to define any methods. In particular, to insert or update an inventory record, the JpaRepositorywill provide a built-in  save method (see usage in InventoryService.java below).

In the case of Eclipse MicroProfile application, this class is custom built using javax.persistence.EntityManager. The setInventory method inserts a new record or updates an existing record. The findAll method returns all records whereas getExisting finds and returns an existing inventory record.  

Spring

package org.springexamples.inventories.dao;

import org.springexamples.inventories.dao.entities.InventoryEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional
public interface InventoryRepository extends 
  JpaRepository<InventoryEntity, Integer> {
}

Eclipse MicroProfile

package org.olpexamples.inventories.dao;

import org.olpexamples.inventories.dao.entities.InventoryEntity;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import java.util.List;

public class InventoryRepository {
    @PersistenceContext
    private EntityManager em;

    public void setInventory(InventoryEntity inventory){
        em.persist(inventory);
    }

    public List<InventoryEntity> findAll(){
        return em.createNamedQuery("InventoryEntity.findAll",
                                   InventoryEntity.class).getResultList();
    }

    public InventoryEntity getExisting(String brand){
        try{
            InventoryEntity existing = 
              em.createNamedQuery("InventoryEntity.findById", 
                                  InventoryEntity.class).
                    setParameter("brand", brand).getSingleResult();
            return existing;
        }catch(NoResultException e){
            return null;
        }
    }
}

Inventory.java

This class represents a car inventory model. It has similar attributes to the InventoryEntity class. For Spring and Eclipse MicroProfile applications this class has an identical implementation (except for the package definition).

Spring

package org.springexamples.inventories.model;

import javax.validation.constraints.NotNull;

public class Inventory {

    @NotNull
    private String brand;

    private Integer inventory;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getInventory() {
        return inventory;
    }

    public void setInventory(Integer inventory) {
        this.inventory = inventory;
    }
}

Eclipse MicroProfile

package org.olpexamples.inventories.model;

import javax.validation.constraints.NotNull;

public class Inventory {

    @NotNull
    private String brand;

    private Integer inventory;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getInventory() {
        return inventory;
    }

    public void setInventory(Integer inventory) {
        this.inventory = inventory;
    }
}

InventoryService.java

This class performs service layer operations on car inventories by invoking appropriate methods on InventoryRepository. It carries out necessary transformations between the Inventory and InventoryEntity classes. The rest package directly interfaces with this class rather than InventoryRepository. This class has the awareness of both Inventory and InventoryEntity classes and provides transformations between them.

The getAllInventories method returns all inventories in the database and has an identical implementation in Spring and Eclipse MicroProfile applications. Similarly, transformToInventoryEntity and transformToInventory methods, which provide transformations between Inventory and InventoryEntity classes, are identical.

The only notable difference is with the setInventory method, which defines inventory for a particular car brand. In the case of Spring, we call save on InventoryRepository, a built-in method supplied by JpaRepository. In the case of Eclipse MicroProfile, following our custom implementation, we try to obtain an existing inventory of the brand and if exists, we update it. Otherwise, we persist a brand new inventory.

Spring

package org.springexamples.inventories.service;

import org.springexamples.inventories.dao.InventoryRepository;
import org.springexamples.inventories.dao.entities.InventoryEntity;
import org.springexamples.inventories.model.Inventory;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collection;
import java.util.stream.Collectors;

@Service
public class InventoryService {
    protected InventoryRepository inventoryRepository;

    @Autowired
    public InventoryService(InventoryRepository inventoryRepository){
        this.inventoryRepository = inventoryRepository;
    }

    public Collection<Inventory> getAllInventories(){
        return inventoryRepository.findAll().stream()
          .map(this::transformToInventory).collect(Collectors.toList());
    }

    public void setInventory(Inventory inventory){
        inventoryRepository.save(transformToInventoryEntity(inventory));
    }

    private InventoryEntity transformToInventoryEntity(Inventory inventory){
        InventoryEntity e = new InventoryEntity();
        e.setBrand(inventory.getBrand());
        e.setInventory(inventory.getInventory());
        return e;
    }

    private Inventory transformToInventory(InventoryEntity entity){
        Inventory inventory = new Inventory();
        inventory.setBrand(entity.getBrand());
        inventory.setInventory(entity.getInventory());
        return inventory;
    }
}

Eclipse MicroProfile

package org.olpexamples.inventories.service;

import org.olpexamples.inventories.dao.entities.InventoryEntity;
import org.olpexamples.inventories.model.Inventory;
import org.olpexamples.inventories.dao.InventoryRepository;
import javax.inject.Inject;
import java.util.Collection;
import java.util.stream.Collectors;

public class InventoryService {
    @Inject
    protected InventoryRepository inventoryRepository;

    public Collection<Inventory> getAllInventories(){
        return inventoryRepository.findAll().stream()
          .map(this::transformToInventory).collect(Collectors.toList());
    }

    public void setInventory(Inventory inventory){
        InventoryEntity existingEntity = inventoryRepository
          .getExisting(inventory.getBrand());
        if(existingEntity == null){
            inventoryRepository
              .setInventory(transformToInventoryEntity(inventory));
        }else{
            existingEntity.setInventory(inventory.getInventory());
            inventoryRepository.setInventory(existingEntity);
        }
    }

    private InventoryEntity transformToInventoryEntity(Inventory inventory){
        InventoryEntity e = new InventoryEntity();
        e.setBrand(inventory.getBrand());
        e.setInventory(inventory.getInventory());
        return e;
    }

    private Inventory transformToInventory(InventoryEntity entity){
        Inventory inventory = new Inventory();
        inventory.setBrand(entity.getBrand());
        inventory.setInventory(entity.getInventory());
        return inventory;
    }
}

InventoryResource.java

This is a controller class that accepts REST calls and translates them to commands to be used by InventoryService. The code here is very similar for both the Spring and Eclipse MicroProfile applications. The URI path cars precedes any other segment defined at the method level. The methods getInventories and setInventory are mapped to the REST calls cars/inventories and cars/setInventory, respectively, and direct the associated call to InventoryService.

In terms of differences, while the Spring application utilizes annotations from the org.springframework.beans.factory.annotation and org.springframework.web.bind.annotation packages, the Eclipse MicroProfile application utilizes annotations from the javax.ws.rs, cdi-api, and javax.inject packages.

Spring

package org.springexamples.inventories.rest;

import org.springexamples.inventories.model.Inventory;
import org.springexamples.inventories.service.InventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;

@RestController
@RequestMapping(value = "/cars")
public class InventoryResource {

    @Autowired
    protected InventoryService service;

    @Autowired
    public InventoryResource(InventoryService service){
        this.service = service;
    }

    @RequestMapping(value = "/inventories", 
                    produces = { "application/json" }, 
                    method= {RequestMethod.GET})
    public Collection<Inventory> getInventories(){
        return service.getAllInventories();
    }

    @RequestMapping(value = "/setInventory", 
                    consumes = { "application/json" }, method= {RequestMethod.POST})
    public void setInventory(@RequestBody Inventory inventory){
        service.setInventory(inventory);
    }
}

Eclipse MicroProfile

package org.olpexamples.inventories.rest;

import org.olpexamples.inventories.model.Inventory;
import org.olpexamples.inventories.service.InventoryService;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Collection;

@RequestScoped
@Path("cars")
public class InventoryResource {
    @Inject
    protected InventoryService service;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Transactional
    @Path("inventories")
    public Collection<Inventory> getInventories(){
        return service.getAllInventories();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Transactional
    @Path("setInventory")
    public Response setInventory(Inventory inventory){
        service.setInventory(inventory);
        return Response.status(Response.Status.NO_CONTENT).build();
    }
}

InventoryApplication.java

This is the main entry point for the application. For Spring, this class performs various configuration tasks. In particular, @EntityScan and @EnableJpaRepositories designate the base packages for JPA entities and repositories, respectively. We also declare the InventoryRepository instance, to be auto-injected by Spring, and define the service() method to initialize the InventoryService and return it. Here we also give the main entry method for the application and indicate the name of its configuration file (inventory-application.yaml, to be reviewed later).

For Eclipse MicroProfile, the InventoryApplication.java file is rather simple. It only declares "/" to be the context path for this application (the path defined here is appended to the context root defined in server.xml, to be discussed later).

Spring

package org.springexamples.inventories.rest;

import org.springexamples.inventories.dao.InventoryRepository;
import org.springexamples.inventories.service.InventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EntityScan("org.springexamples.inventories.dao.entities")
@EnableJpaRepositories("org.springexamples.inventories.dao")
public class InventoryApplication {

    @Autowired
    protected InventoryRepository inventoryRepository;

    public static void main(String[] args) {
        System.setProperty("spring.config.name", "inventory-application");
        SpringApplication.run(InventoryApplication.class, args);
    }

    @Bean
    public InventoryService service(){
        return new InventoryService(inventoryRepository);
    }
}

Eclipse MicroProfile

package org.olpexamples.inventories.rest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/")
    public class InventoryApplication extends Application {
}

Auxiliary Files

Spring

logback.xml: This is a simple log configuration file. Details are omitted. Please see the git repository that contains all the files discussed in this article.

inventory-application.yaml: This configuration file is referenced by InventoryApplication.java as discussed above. The environment variables DB_HOST, DB_PORT, and SERVER_PORT correspond to the database server host, database server port, and server port number for the Spring application to listen to. Those environment variables can be defined in the shell or passed to Docker executable in command, as will be discussed below. The database user name and password are hardcoded in the file, which is acceptable for our purposes. However, in a real enterprise application, those would be supplied in a more secure way, e.g. as environment variables specific to development, QA, production environments.

spring:
  application:
    name: inventory-service
  datasource:
    url: jdbc:derby://${DB_HOST}:${DB_PORT}/CarDB;create=false
    username: demo
    password: demopwd
    driver-class-name: org.apache.derby.jdbc.ClientDriver
  jpa:
    hibernate:
      ddl-auto: none

# HTTP Server
server:
  servlet:
    context-path: /CarInventories
  port: ${SERVER_PORT}

Eclipse MicroProfile

beans.xml: This is the beans archive descriptor as required by the CDI specification. This file could be used to employ interceptor, decorator, or alternative mechanisms specific to CDI. As we do not have any such mechanism in our simple application we have an empty beans element declaration.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="all">
</beans>

web.xml: This is the standard Java EE web deployment descriptor. We supply an empty descriptor as no specific provisions are needed for our purposes.  

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">
</web-app>

server.xml: This file is used to configure various aspects of Open Liberty server.

  • The featureManager element includes the individual Eclipse MicroProfile functional components used in our application. 

  • The keystore element is used for the keystore service configuration in server. See SSL Configuration Attributes for details.

  • The httpEndpoint element helps to configure the HTTP endpoints for the Open Liberty server. The env.SERVER_PORT parameter indicates that the HTTP port will be set according to the SERVER_PORT environment variable, whereas the default.https.port parameter indicates that the HTTPS port will be supplied as a startup parameter to Open Liberty server (to be set in the pom.xml file, see below).

  • The webApplication element is where we define the application context root via app.context.root parameter, which will be set in pom.xml and passed to Open Liberty during startup.

  • The library element allows us to define libraries for the Derby database. The shared.resource.dir parameter corresponds to liberty/wlp/usr/shared/resources/ under the build directory where we copy the Derby library jars during the Maven build as configured via the copy-derby-dependency task in pom.xml. This folder will also need to be defined when the Docker executable is run, to be explained later.

  • The dataSource element is used to define the data source. Here, the user id and password for the Derby network database are mentioned along with server hostname and port. The env.DB_HOST and env.DB_PORT parameters indicate that the server host and port will be passed as environment variables to Open Liberty server.

  • The same comments on the database user name and password in the Spring application apply here.

<server description="Inventory Liberty server">

    <featureManager>
        <feature>jaxrs-2.1</feature>
        <feature>jsonp-1.1</feature>
        <feature>cdi-2.0</feature>
        <feature>jpa-2.2</feature>
    </featureManager>
    <keyStore id="defaultKeyStore" password="ignoreit" />

    <httpEndpoint host="*" httpPort="${env.SERVER_PORT}" 
                  httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>

    <webApplication location="inventories.war" 
                    contextRoot="${app.context.root}"/>

    <!-- Derby Library Configuration -->
    <library id="derbyJDBCLib">
        <fileset dir="${shared.resource.dir}" includes="derby*.jar"/>
    </library>

    <!-- Datasource Configuration -->
    <dataSource id="inventoryDatasource"
                jndiName="jdbc/inventoryDatasource">
        <jdbcDriver libraryRef="derbyJDBCLib" />
        <properties.derby.client databaseName="CarDB" createDatabase="false"
        serverName="${env.DB_HOST}" portNumber="${env.DB_PORT}" 
                                 user="demo" password="demopwd"/>
    </dataSource>
</server>

persistence.xml: This file defines the persistence unit used in our application according to the Java Persistence API specification. Observe that it references the dataSource element defined in server.xml above. Note that we use EclipseLink as the JPA provider for the Derby network database and hence the eclipselink related property definitions.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="jpa-unit" transaction-type="JTA">
        <jta-data-source>jdbc/inventoryDatasource</jta-data-source>
        <properties>
            <property name="eclipselink.ddl-generation" value="create-tables"/>
            <property name="eclipselink.ddl-generation.output-mode" 
                      value="both" />
        </properties>
    </persistence-unit>
</persistence>

Dockerfile

Spring

This is as simple as it gets. Our image will be built on the openjdk:8-jdk-alpine image. The original archive will be copied to app.jar and run as an executable jar.

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Eclipse MicroProfile

The Docker image for Eclipse MicroProfile application will be built on open-liberty image. We create two links, servers and resources, in order to make command line instruction less verbose (to be seen later). The servers link is needed for Open Liberty server resources, which also contain all the libraries for Eclipse MicroProfile functional components. The resources link is needed for any additional libraries, e.g. derby driver and JPA provider.

FROM open-liberty
RUN ln -s /opt/ol/wlp/usr/servers /servers
RUN ln -s /opt/ol/wlp/usr/shared/resources /resources
ENTRYPOINT ["/opt/ol/wlp/bin/server", "run"]
CMD ["defaultServer"]

pom.xml

Spring

The Maven coordinates of our application are spring-examples:inventories:1.0-SNAPSHOT. We use the Spring version 2.1.2 and Spring Cloud Greenwich releases.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                        http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.2.RELEASE</version>
  </parent>
  <groupId>spring-examples</groupId>
  <artifactId>inventories</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <start-class>
      org.springexamples.inventories.rest.InventoryApplication
    </start-class>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Greenwich.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
...

The pom.xml file continues with Spring, Spring Data Commons, Spring Cloud, and Spring Data JPA dependencies.

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-commons</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
  ...

Finally, we declare dependencies for the Derby database.

    <dependency>
      <groupId>org.apache.derby</groupId>
      <artifactId>derby</artifactId>
      <version>10.14.2.0</version>
      </dependency>

    <dependency>
      <groupId>org.apache.derby</groupId>
      <artifactId>derbyclient</artifactId>
      <version>10.10.1.1</version>
    </dependency>
  </dependencies>
...

Lastly, we provide build plugins. The first one is the Spring Maven Plugin. The other one is the Dockerfile Maven Plugin from Spotify. Note that konuratdocker/spark-examples is the name of my personal Docker repository, to be replaced with yours if you intend to run those examples. For the Spring application, we name the Docker image inventory-service_spring.

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
          <execution>
          <goals>
            <goal>repackage</goal>
          </goals>
          </execution>
        </executions>
      </plugin>
        <plugin>
          <groupId>com.spotify</groupId>
          <artifactId>dockerfile-maven-plugin</artifactId>
          <version>1.3.6</version>
          <configuration>
            <tag>inventory-service_spring</tag>
            <repository>konuratdocker/spark-examples</repository>
            <imageTags>
              <imageTag>inventory-service_spring</imageTag>
              </imageTags>
            <buildArgs>
            <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
            </buildArgs>
          </configuration>
          </plugin>
    </plugins>
  </build>
</project>

Eclipse MicroProfile

The pom.xml file closely follows the conventions for the Open Liberty server (for examples, see this blog.) The Maven coordinates for our application, in this case, are olp-examples:inventories:1.0-SNAPSHOT. Here we define CarInventories as the name of our application (app.name tag) also used as the root context for the web application (the warContext tag). The usr packaging type indicates that the generated package does not include an Open Liberty server runnable.

<?xml version='1.0' encoding='utf-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
      http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>olp-examples</groupId>
    <artifactId>inventories</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>
        UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <app.name>CarInventories</app.name>
        <testServerHttpPort>9080</testServerHttpPort>
        <testServerHttpsPort>9443</testServerHttpsPort>
        <warContext>${app.name}</warContext>
        <package.file>
        ${project.build.directory}/${app.name}.zip</package.file>
        <packaging.type>usr</packaging.type>
    </properties>
...

The dependencies are declared next. As indicated in this blog, the Open Liberty Bill of Materials dependency, referenced in the dependencyManagement section, allows us to declare individual Open Liberty features without explicit reference to their versions. Each feature corresponds to an Eclipse MicroProfile functional component. We finally declare the Derby database dependencies.

   <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.openliberty.features</groupId>
                <artifactId>features-bom</artifactId>
                <version>RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
      <!--Open Liberty features -->
        <dependency>
            <groupId>io.openliberty.features</groupId>
            <artifactId>jaxrs-2.1</artifactId>
            <type>esa</type>
        </dependency>
        <dependency>
            <groupId>io.openliberty.features</groupId>
            <artifactId>jsonp-1.1</artifactId>
            <type>esa</type>
        </dependency>
        <dependency>
            <groupId>io.openliberty.features</groupId>
            <artifactId>cdi-2.0</artifactId>
            <type>esa</type>
        </dependency>
        <dependency>
            <groupId>io.openliberty.features</groupId>
            <artifactId>jpa-2.2</artifactId>
            <type>esa</type>
        </dependency>
      <!-- Derby -->
        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>10.14.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derbyclient</artifactId>
            <version>10.10.1.1</version>
        </dependency>
    </dependencies>
...

The build section consists of various plugins. The first one is the Maven Dependency Plugin used to copy Derby database libraries under a resources folder (recall from above that we had created a symbolic link in Dockerfile to reference the resources folder later on; that will be further discussed in Part 2).

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>copy-derby-dependency</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <includeArtifactIds>derby,derbyclient</includeArtifactIds>
                            <outputDirectory>${project.build.directory}/liberty/wlp/usr/shared/resources/</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
...

Next, we continue with a plugin to deploy and manage our application within the Open Liberty server. For details see the original source. Here, observe that the previously declared default ports and the web application context root are passed to the Open Liberty server during startup.

            <!-- Enable liberty-maven plugin -->
            <plugin>
                <groupId>net.wasdev.wlp.maven.plugins</groupId>
                <artifactId>liberty-maven-plugin</artifactId>
                <version>2.6.1</version>
                <configuration>
                    <assemblyArtifact>
                        <groupId>io.openliberty</groupId>
                        <artifactId>openliberty-runtime</artifactId>
                        <version>RELEASE</version>
                        <type>zip</type>
                    </assemblyArtifact>
                    <configFile>src/main/liberty/config/server.xml
                    </configFile>
                    <packageFile>${package.file}</packageFile>
                    <include>${packaging.type}</include>
                    <bootstrapProperties>
                        <default.http.port>
                           ${testServerHttpPort}
                        </default.http.port>
                        <default.https.port>
                           ${testServerHttpsPort}
                        </default.https.port>
                        <app.context.root>
                           ${warContext}
                        </app.context.root>
                    </bootstrapProperties>
                </configuration>
                <executions>
                    <execution>
                        <id>install-liberty</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>install-server</goal>
                        </goals>
                    </execution>
                    <!-- Create defaultServer -->
                    <execution>
                        <id>create-server</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>create-server</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>install-app</id>
                        <phase>package</phase>
                        <goals>
                            <goal>install-apps</goal>
                        </goals>
                        <configuration>
                            <appsDirectory>apps</appsDirectory>
                            <stripVersion>true</stripVersion>
                            <installAppPackages>
                            project
                            </installAppPackages>
                        </configuration>
                    </execution>
                    <execution>
                        <id>start-server</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start-server</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>stop-server</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop-server</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>package-app</id>
                        <phase>package</phase>
                        <goals>
                            <goal>package-server</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
...

The final plugin we reference is Dockerfile Maven Plugin from Spotify, utilized in exactly the same way as in Spring application. The only difference is that here the Docker image is named inventory-service_ol.

            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>dockerfile-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <tag>inventory-service_ol</tag>
                    <repository>konuratdocker/spark-examples</repository>
                    <imageTags>
                        <imageTag>inventory-service_ol</imageTag>
                    </imageTags>
                    <buildArgs>
                        <JAR_FILE>
                        target/${project.build.finalName}.jar
                        </JAR_FILE>
                    </buildArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

That's all for Part 1! Tune back in tomorrow when we test and run our two applications. 

Spring Framework Eclipse application Database Relational database Docker (software) Framework microservice Inventory (library) Web Service

Opinions expressed by DZone contributors are their own.

Related

  • 7 Microservices Best Practices for Developers
  • Spring Reactive Microservices: A Showcase
  • Practical Transaction Handling in Microservice Architecture
  • Fixing Your Microservices Architecture Using Graph Analysis

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!