DEV Community

David Salter
David Salter

Posted on • Originally published at davidsalter.uk

Creating a TomEE / React Application using MicroProfile - Part 1

In this and the following articles, I'm going to build a TomEE application that provides a REST interface. The interface will then be queried by a React JavaScript frontend. Specifically, the back end will expose a REST interface via JAX-RS providing information about Constellations. The React front end will allow users to select a Constellation to get more details about it. The final product will look something like:

Constellations - TomEE and React Demonstration

Creating The Back End

The quickest way to create the Constellations API is to use the MicroProfile Starter. This enbles all the scaffolding to be created for a MicroProfile Maven application. For this application the followng options were chosen:

  • groupId - com.davidsalter
  • artifactId - constellationapi
  • MicroProfile Version - 2.1
  • MicroProfile Server - TomEE

At the time of writing, the latest available version of TomEE on the MicroProfile Starter is 8.0.0.M3, however version 8.0.0 has been released since the starter was last updated. The first task therefore, is to update to the correct (and latest) version of TomEE in the project's pom.xml file.

<properties>
  <tomee.version>8.0.0</tomee.version>
...

<plugin>
  <groupId>org.apache.tomee.maven</groupId>
  <artifactId>tomee-maven-plugin</artifactId>
  <version>${tomee.version}</version>

To easily access the constellations within the API, I want to store them in a database and query them using JPA. To do this, I need to add some of Java EE 8 into the application. This is achieved by adding the javaee-api dependency into the pom.xml

<dependency>
  <groupId>org.apache.tomee</groupId>
  <artifactId>javaee-api</artifactId>
  <version>8.0-2</version>
  <scope>provided</scope>
</dependency>

The full pom.xml looks like:

<?xml version="1.0" encoding="UTF-8"?>
<project
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.davidsalter</groupId>
  <artifactId>constellationapi</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
  <properties>
    <final.name>constellationapi</final.name>
    <tomee.version>8.0.0</tomee.version>
    <failOnMissingWebXml>false</failOnMissingWebXml>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.eclipse.microprofile</groupId>
      <artifactId>microprofile</artifactId>
      <version>2.1</version>
      <type>pom</type>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.tomee</groupId>
      <artifactId>javaee-api</artifactId>
      <version>8.0-2</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>constellationapi</finalName>
  </build>
  <profiles>
    <profile>
      <id>tomee</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.tomee.maven</groupId>
            <artifactId>tomee-maven-plugin</artifactId>
            <version>${tomee.version}</version>
            <executions>
              <execution>
                <id>executable-jar</id>
                <phase>package</phase>
                <goals>
                  <goal>exec</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <context>ROOT</context>
              <tomeeClassifier>microprofile</tomeeClassifier>
              <tomeeHttpPort>8080</tomeeHttpPort>
              <tomeeShutdownPort>8005</tomeeShutdownPort>
              <tomeeAjpPort>8009</tomeeAjpPort>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>

Now that we've created the basic scaffolding for the project, using the MicroProfile Starter and manually editing the pom.xml, there are 3 things we need to do to complete the API.

  1. Create a persistence layer to hold details of all the Constellations
  2. Populate the persistence layer
  3. Create a JAX-RS endpoint to query the persistence layer

Create A Persistence Layer

Since we're using JPA, we need to create a @Entity class to represent a Constellation and a DAO class to use to retrieve data from the backend database table. Since I'm using TomEE, I can use the embedded HSQLDB and don't need to install any new drivers. All the required dependencies are provided out of the box.

First off, we need to create an @Entity class

package com.davidsalter.constellationapi.entity;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name = "Constellation")
@NamedQuery(name = "Constellation.findAll", query = "SELECT c FROM Constellation c")
public class Constellation {
  private static final long serialVersionUID = 1L;

  public Constellation() {
  }

  private String name;

  @Id
  private String abbreviation;

  private String description;

  // Getters / Setters omitted

From this code, we can see that an @Entity class has been defined that is backed off by a database table called Constellation This table has 3 fields


Field Description
name The name of a constellation.
abbreviation The abbreviation for the constellation. This is the PK
description The description for the constellation,

The class also defines a single @NamedQuery to get a list of all of the Constellations form the database.

So, the Constellation class represents a single entity, so we need to write a DAO class to be able to retrieve all the entities from the database.

The ConstellationDao class looks like the following:

package com.davidsalter.constellationapi.controller;

import java.util.List;

import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.davidsalter.constellationapi.entity.Constellation;

@RequestScoped
public class ConstellationDao {

  @PersistenceContext(name = "constellation-pu")
  private EntityManager em;

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

This class simply provides one method (findAllConstellations) that executes the @NamedQuery we created within the Constellation class.

The class makes use of an EntityManager to query the database. This in turn uses a persistence unit called constellation-pu to define the database and how it is created.

Within the META-INF/persistence.xml file, we define the persistence unit as follows:

<persistence version="2.2"
  xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="constellation-pu">
    <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
    </provider>

    <jta-data-source>constellationDatasource</jta-data-source>
    <properties>
      <property name="openjpa.jdbc.DBDictionary" value="hsql" />

      <property
        name="javax.persistence.schema-generation.database.action"
        value="drop-and-create" />
      <property
        name="javax.persistence.schema-generation.create-source"
        value="metadata" />
      <property
        name="javax.persistence.schema-generation.drop-source"
        value="metadata" />
      <property name="javax.persistence.sql-load-script-source"
        value="META-INF/import.sql" />
    </properties>
  </persistence-unit>
</persistence>

This persistence unit specifies that it uses a JTA data source called constellationDatasource. We'll see how this is defined in a minute. For now though, lets look at the properties that we define for the data source.

First of all, we tell OpenJPA that we are using a HSQL database. We then define that the schema must be dropped and created (drop-and-create) everytime the application is run. In this example application, we are seeding the database everytime we run the app as we're going to use an in-memory database. This technique of dropping and creating a database is most likely, not suitable for production use !

The next two properties tell JPA how to create and drop the database table(s). In this case, to use the metadata defined for the Consteallation class, rather than via user defined scripts.

Finally, the sql-load-script-source tells JPA to run the script META-INF/import.sql to seed the database. We'll look at this a bit later.

So, the persistence unit specifies that we are to use a JTA datasource called constellationDatasource, but where is that defined ? In TomEE, datasources can be defined within a file called META-INF/resources.xml In this instance, as below:

<Resource id="constellationDatasource" type="javax.sql.DataSource">
    defaultAutoCommit = true
    jdbcDriver = org.hsqldb.jdbcDriver
    jdbcUrl = jdbc:hsqldb:mem:hsqldb
    jtaManaged = true
    maxActive = 20
    password =
    passwordCipher = PlainText
    userName = sa
</Resource>

This code simply defines an in-memory hsql database that is accessed via a datasource called constellationDatasource.

Populate Persistence Layer

We looked at this briefly before. Within the persistence unit, JPA allows us to specify a script that is run whenever the application starts up to seed the database with pre-defined data.

<property name="javax.persistence.sql-load-script-source"
  value="META-INF/import.sql" />

To load the constellations data into the database, this file contains an INSERT statement for each Constellation, e.g.

insert into constellation(name, abbreviation, description) values ('Andromeda', 'And', 'The Chained Maiden');
insert into constellation(name, abbreviation, description) values ('Antila', 'Ant', 'The Air Pump');
etc.

Remember we don't have to create the database table as that is done by JPA, and we don't need to worry about duplicate entries in the database as JPA drops and creates the schema everytime the application (and the script) is executed.

Create JAX-RS Endpoint

We're nearly there !

In the previous sections, we've created a skeleton application, created a persistence layer and seeded the database with data. All that's left to do now is to develop a REST endpoint to query the data.

To use JPA, we need to define an Application that specifies the base URI of the endpoint. The MicroProfile Starter did this for us automatically within the ConstellationapiRestApplication class

package com.davidsalter.constellationapi;

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

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

This class simply defines that the base URI of our endpoint will be /api

To implement an endpoint, we need to write a Resource that uses the DAO we created earlier to query the database. This is shown below:

package com.davidsalter.constellationapi.boundary;

import java.util.List;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.davidsalter.constellationapi.controller.ConstellationDao;
import com.davidsalter.constellationapi.entity.Constellation;

@Path("constellations")
@RequestScoped
public class ConstellationResource {

  @Inject
  private ConstellationDao constellationDao;

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public List<Constellation> getConstellations() {
    return constellationDao.findAllConstellations();
  }
}

This class simply injects an instance of our ConstellationDao, calls the method findAllConstellations on it, and then returns the result as JSON to the caller.

OK. So we're there?

We've build an endpoint that will return the required data. We can build and execute the endpoint with:

$ mvn clean package tomee:run

and then test it with:

$ curl http://localhost/api/constellations
[{"abbreviation":"And","description":"The Chained Maiden","name":"Andromeda"},
etc...

All looks good so far. However, since our React website is running separately from TomEE, we'll need to configure CORS within TomEE to enable the React application to call the TomEE web services. We'll see how this can be done in Part 2.

Top comments (0)