Java EE 8 High Performance

4.7 (3 reviews total)
By Romain Manni-Bucau
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Money – The Quote Manager Application

About this book

The ease with which we write applications has been increasing, but with this comes the need to address their performance. A balancing act between easily implementing complex applications and keeping their performance optimal is a present-day need. In this book, we explore how to achieve this crucial balance while developing and deploying applications with Java EE 8.

The book starts by analyzing various Java EE specifications to identify those potentially affecting performance adversely. Then, we move on to monitoring techniques that enable us to identify performance bottlenecks and optimize performance metrics. Next, we look at techniques that help us achieve high performance: memory optimization, concurrency, multi-threading, scaling, and caching. We also look at fault tolerance solutions and the importance of logging. Lastly, you will learn to benchmark your application and also implement solutions for continuous performance evaluation.

By the end of the book, you will have gained insights into various techniques and solutions that will help create high-performance applications in the Java EE 8 environment.

Publication date:
January 2018
Publisher
Packt
Pages
350
ISBN
9781788473064

 

Chapter 1. Money – The Quote Manager Application

Before working on evaluating and enhancing the performance of your application, you need to indeed have an application. In this part, we will create a small application that we will use to illustrate every part of the book. This chapter doesn't intend to explain all the steps required to create a Java EE application. It will give you the overall steps and ensure that the references to the steps will be obvious later.

The use case of this application will be a microservice that provides a set of web services to manage stocks and shares. This chapter will, therefore, introduce you to the application environment:

  • Application code structure
  • Database setup
  • Data persistence
  • Exposing data over HTTP
  • Deploying your application
 

Setting up the environment


Before starting with writing code, make sure that you have an environment ready to work with Java EE. We need a Java Virtual Machine 8 (JVM 8) and, more particularly, the Java Development Kit 8 (JDK 8). As a quick reminder, Java EE version V is based on Java Standalone Edition (Java SE) version V as well. You can download the JDK on the Oracle website (http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html).

Note

Alternatively, you can download the OpenJDK version of the JDK on the OpenJDK project website (http://openjdk.java.net/install/), but I recommend that you use the Oracle version. We will discuss this later in the book.

Don't forget to accept the license agreement and select the right distribution for your operating system (Windows, Linux, or macOS).

Now that we have a JDK, we need a tool to build our application and convert it into a format that we will be able to deploy in our Java EE server. This book will use Apache Maven (https://maven.apache.org/) to build the application. It can be downloaded on the Apache Maven download page (https://maven.apache.org/download.cgi). We need the binary distribution; Linux users have to select the tar.gz format while Windows users have to select the .zip archive.

At this point, we have everything we need to create our application. You will probably want to have an Integrated Development Environment (IDE), such as NetBeans (https://netbeans.org/), Eclipse (https://eclipse.org/ide/), or Intellij Idea (https://www.jetbrains.com/idea/). Since this book is more about performance than development, we won't go into much detail about IDEs. If you need one, just select the one you are the most familiar with.

To ensure that the environment is ready, we will set variables to define where to find the software without having to use the full path to the binary or script each time. JAVA_HOME will point to the folder you extracted from the JDK, and MAVEN_HOME will point to the folder you extracted from the Apache Maven archive. Here is an example for Linux (replace export with set for a DOS shell):

$ export JAVA_HOME=/home/developer/jdk1.8.0_144
$ export MAVEN_HOME=/home/developer/apache-maven-3.5.0

Now, we need to ensure that the JDK and Maven tools are available. For this, we add them to PATH on Linux and Path on Windows:

# On Linux
$ export PATH=$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH

# On Windows
$ set Path=%JAVA_HOME%\bin;%MAVEN_HOME%\bin;%Path%

You can validate your setup by executing the following command:

$ mvn -version
Maven home: /home/developer/apache-maven-3.5.0
Java version: 1.8.0_144, vendor: Oracle Corporation
Java home: /home/developer/jdk1.8.0_144/jre
Default locale: fr_FR, platform encoding: UTF-8
OS name: "linux", version: "4.10.0-32-generic", arch: "amd64", family: "unix"

To run a Java EE application, we also need a container, such as GlassFish, WildFly, WebSphere Liberty Profile, or Apache TomEE. The deployment being specific and Java EE 8 being very recent, we will use GlassFish in this book.

Finally, to get everything ready, we will use a database. We will use MySQL as a very common case, but any other relational database will work as well. You can download MySQL from https://dev.mysql.com/downloads/mysql/, but most Linux distributions will have a package ready to install. For instance, on Ubuntu you can just execute the following line:

sudo apt install mysql-server
 

The application architecture


Our application will import some stock quotations daily; it will then expose them and allow you to update them through a web service.

To implement it, we will use a standard Java EE architecture:

  • The persistence layer will use JPA 2.2 and store the data in a MySQL database.
  • A service layer will implement the business logic and orchestrate the persistence layer. It will rely on the following:
    • Java Transaction API (JTA) 1.2 for transactionality
    • Context and Dependency Injection 2.0 (CDI) for Inversion of Control (IoC)
    • Bean Validation 2.0 for validations
  • A front layer will expose a part of the service layer through HTTP. It will rely on the following:
    • JAX-RS 2.1 for stateless endpoints
    • WebSocket 1.1 for stateful communications
    • JSON-B 1.0 for marshalling/unmarshalling

Here is a picture summarizing this structure:

 

Application project highlights


To be able to create and run this application, we will need to set up a build tool. For this book, it will be Apache Maven; however, Gradle, Ant, or any other alternative will work perfectly as well. We will then identify some key parts of the application code and, finally, we will insert some data to ensure that our application is usable before investigating its performance.

The build

The only dependency Java EE requires is the Java EE API:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>${javaee-api.version}</version> <!-- 8.0 -->
  <scope>provided</scope>
</dependency>

Note

If you prefer, you can indeed register all the individual specifications, but it will require more work to maintain the list with Java EE upgrades. For this reason, the bundle is often preferred.

Here, the point is to ensure that the API is provided, which means it will not be packaged in the deliverable and will inherit from the server API. The server providing the services associated with the API also provides the API with the right supported version and the right defaults matching the built-in implementations.

Since Java EE 6, there are two main flavors of Java EE: the web profile and the full profile. The web profile is a light version, with only half the specifications compared with the full profile, more or less. The web profile supports only web applications and, therefore, war files. Most of this book will work with a web profile server, so we will package our application as war:

<packaging>war</packaging>

Since we need Java 8, don't forget to configure the Java source and target version in the build. It can be done in different ways, but configuring maven-compiler-plugin as follows is an efficient one:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.6.1</version>
  <configuration>
    <source>1.8</source>
    <target>1.8</target>
  </configuration>
</plugin>

The persistence layer

Our data model will be simple: a quote will be linked to a customer. This means that a customer can see a set of quotes, and quotes can be seen by a set of customers. In terms of use cases, we want to be able to monetize our API and make the customers pay to access some quote prices. To do so, we will need a sort of whitelist of quotes per customer.

JPA uses a descriptor called persistence.xml, placed in the META-INF repository of resources (or WEB-INF), which defines how EntityManager, which is a class that allows the manipulation of our model, will be instantiated. Here is what it looks like for our application:

<persistence 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"
             version="2.2">
  <persistence-unit name="quote">
    <class>com.github.rmannibucau.quote.manager.model.Customer</class>
    <class>com.github.rmannibucau.quote.manager.model.Quote</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="avax.persistence.schema
      -generation.database.action" value="create"/>
    </properties>
  </persistence-unit>
</persistence>

The link between the database and the Java code is done through entities. An entity is a plain old java object (POJO) that is decorated with the javax.persistenceannotations. They mainly define the mapping between the database and the Java model. For instance, @Idmarks a Java field that must match the database identifier.

Here is an example of our Quote entity:

@Entity
public class Quote {
    @Id
    @GeneratedValue
    private long id;

    private String name;

    private double value;

    @ManyToMany
    private Set<Customer> customers;

    // getters/setters
}

This simple model implicitly defines a QUOTE table with three columns, ID, NAME, and VALUE (the casing can depend on the database), and a table to manage the relationship with the CUSTOMER table, which is named  QUOTE_CUSTOMER by default.

In the same spirit, our Customer entity just defines an identifier and name as columns and also the reverse relationship to the Quote entity:

@Entity
public class Customer {
    @Id
    @GeneratedValue
    private long id;

    private String name;

    @ManyToMany(mappedBy = "customers")
    private Set<Quote> quotes;

    // getters/setters
}

What is important here is to notice the relationships in the model. We will deal with this later on.

The service layer

The goal of the book being to discuss the performance and not how to write a Java EE application, we will not detail the whole service layer here. However, to ensure a common knowledge of what we are dealing with, we will illustrate the code with one service.

We are using JTA 1.2 with JPA 2.2 to establish a link between our database and the Java model. The QuoteService bean, responsible for managing the Quote persistence, can therefore look like the following:

@Transactional
@ApplicationScoped
public class QuoteService {
    @PersistenceContext
    private EntityManager entityManager;

    public Optional<Quote> findByName(final String name) {
        return entityManager.createQuery("select q from Quote q where
        q.name = :name", Quote.class)
                .setParameter("name", name)
                .getResultStream()
                .findFirst();
    }

    public Optional<Quote> findById(final long id) {
        return Optional.ofNullable(entityManager.find(Quote.class, id));
    }

    public long countAll() {
        return entityManager.createQuery("select count(q) from Quote
        q", Number.class)
                .getSingleResult()
                .longValue();
    }

    public Quote create(final Quote newQuote) {
        entityManager.persist(newQuote);
        entityManager.flush();
        return newQuote;
    }

    // ... other methods based on the same model
}

JPA may or may not be used in a transactional context, depending on the kind of operation you do. When you read data, you can often do it without any transaction until you need some lazy loading. However, when you write data (insert/update/delete entities), JPA requires a running transaction to be able to execute the action. This is to ensure consistency of data but also has some implications on the code. To respect that requirement, and have an active transaction, we use @Transactional on methods instead of relying on Enterprise Java Bean3.2 (EJB 3.2), so we can reuse the power of CDI (@ApplicationScoped, for instance, which will avoid creating a new instance per injection).

Our finders are very simple and directly use the EntityManager API. The only new thing Java 8 brings us in this code is the ability to wrap the result with Optional which offers a programmatic way to deal with the presence or absense of the entity instead of relying on a null check. Concretely, the caller can use our finder this way:

final int quoteCount = getCustomer().getCountFor("myquote");
final double quotesPrice = quoteService.findByName("myquote")
    .map(quote -> quote.getValue() * quoteCount)
    .orElse(0);

This kind of code hides the conditional branches behind a fluent API, which makes it more expressive and readable, while the lambdas stay small enough.

Finally, we used inline queries in this code, not static ones like in the @NamedQuery API.

The JAX-RS layer

If we step back one second and think about which stopover the application will execute, we can identify a few of them:

  • HTTP communication handling
  • Payload (un)marshalling
  • Routing
  • Service invocation

Because of the separation of concern principles, or simply for technical constraints between layers, it is very common to use a Data Transfer Object between the JAX-RS/front layer and the CDI/business layer. Of course, this statement can be applied to the business sub-layers as well, but in the case of this book, we will just do it in the JAX-RS layer. To make it obvious in the book, we will prefix the JAX-RS model with Json. Check out the following code snippet:

@JsonbPropertyOrder({"id", "name", "customerCount"})
public class JsonQuote {
    private long id;
    private String name;
    private double value;

    @JsonbProperty("customer_count")
    private long customerCount;

    // getters/setters
}

In this context, the front layer role is to delegate most of the logic to the service layer and convert the business model to the front model (it can almost be seen as a Java to JavaScript conversion for a lot of modern applications):

@Path("quote")
@RequestScoped
public class QuoteResource {
    @Inject
    private QuoteService quoteService;

    @GET
    @Path("{id}")
    public JsonQuote findById(@PathParam("id") final long id) {
        return quoteService.findById(id) // delegation to the business
        layer
                .map(quote -> { // the model conversion
                    final JsonQuote json = new JsonQuote();
                    json.setId(quote.getId());
                    json.setName(quote.getName());
                    json.setValue(quote.getValue());

        json.setCustomerCount(ofNullable(quote.getCustomers())
        .map(Collection::size).orElse(0));
                    return json;
                })
                .orElseThrow(() -> new
                WebApplicationException(Response.Status.NO_CONTENT));
    }

    // other methods
}

Note

We set the JAX-RS @ApplicationPath to /api to ensure that our endpoints are deployed under the /api subcontext.

The WebSocket layer

Why use JAX-RS and WebSocket? Don't they serve the same purpose? Not exactly, in fact, it is becoming more and more common to use both in the same application even if WebSocket is still a bit recent.

JAX-RS (and, more generally, HTTP/1 and the brand new HTTP/2) is generally web application oriented. Understand that it is often used for applications with a user interface (which needs to be compatible with all browsers). It is also commonly used in environments where you cannot assume much about the network setup. More particularly, in environments where you cannot assume the network setup, the proxies will let WebSocket connections work properly (either preventing them completely or disconnecting them too early). The last common case where HTTP-based solutions make a lot of sense is to try to target a market where clients can be developed in any language (Java, Python, Ruby, Go, Node.js, and so on). The fact that the technology is today spreading all over the world and works well with stateless connections, makes it easier to get started with, and it is therefore more accessible than WebSocket, which requires some care from client developers.

However, WebSocket will fit cases where you have higher performance or reactivity constraints, a state to maintain in order to handle the business use case, or you simply want to push the information from the server without requiring a client operation (such as polling).

When you start using a connected protocol such as WebSocket, the first thing to define is your own communication protocol: the format of the message you send/receive and the order of the messages (if needed).

Our WebSocket layer will be responsible for enabling a client to quickly access the quote prices. Therefore, we will react on a client's request (it will contain the name of the quote that we want to get the price for) and we will respond with two pieces of information: whether we found the quote and the current price, if existing.

Then, you need to pick a format to prepare the content sent through the WebSocket over the wire. Here, the choice is often guided by a trade-off between the client (consumers of the service), the requirements, the performances, and the ease of implementation. In our case, we will consider that our clients can be written in Java as well as in JavaScript. That is why we will use JSON.

To summarize the protocol, here is a full communication round-trip, as shown in the following diagram:

The communication protocol is based on a single message type in our case, so a full client/server communication looks like these steps:

  1. The client will connect to the server.
  2. The client will request the price of a quote N times, based on its symbol (name/identifier).
  3. Assuming there is no I/O error or timeout, the client will trigger a disconnect, which will end the communication.

In terms of code, we need multiple bricks of Java EE and we need the following to put them together:

  • The WebSocket API, obviously
  • JSON-B (we could use JSON-P, but it is less friendly) for the Java to JSON conversion
  • CDI, to link the WebSocket to the business layer

To start easy, we can modelize our payloads. Our request has only one nameattribute, so JSON-B allows us to define it this way:

public class ValueRequest {
    private String name;

    // getter/setter
}

On the other side (that is, the response), we have to return a value attribute with the price of the quote and a found Boolean marking value as filled or not. Here again, JSON-B allows us to do a direct mapping of this model with a plain POJO:

public static class ValueResponse {
    private double value;
    private boolean found;

    // getters/setters
}

Now, we need to ensure that the WebSocket will be able to deserialize and serialize these objects as required. The specification defines Encoder and Decoder APIs for this purpose. Since we will back our implementation by JSON-B, we can directly implement it using the (I/O) stream flavors of these APIs (called TextStream). Actually, before doing so, we need to get a Jsonb instance. Considering that we have already created one and made it available in CDI, we can then simply inject the instance in our coders:

@Dependent
public class JsonEncoder implements Encoder.TextStream<Object> {
    @Inject
    private Jsonb jsonb;

    @Override
    public void encode(final Object o, final Writer writer) throws EncodeException, IOException {
        jsonb.toJson(o, writer);
    }

    // other methods are no-op methods
}

The decoding side is now fast to develop, thanks to the JSON-B API, which fits this usage very well with its fromJson()API. We will just note that this side is specific to ValueRequest,since we need to specify the type to instantiate it (compared with the encoding side, which can determine it dynamically):

@Dependent
public class RequestDecoder implements Decoder.TextStream<ValueRequest> {
    @Inject
    private Jsonb jsonb;

    @Override
    public ValueRequest decode(final Reader reader) throws DecodeException, IOException {
        return jsonb.fromJson(reader, ValueRequest.class);
    }

    // other methods are no-op methods
}

Now that we have a way to handle our messages, we need to bind our WebSocket endpoint and implement the @OnMessage method to find the price and send it back to the client relying on our business layer. In terms of implementation, we will react to a ValueRequestmessage, try to find the corresponding quote, fill the response payload, and send it back to the client:

@Dependent
@ServerEndpoint(
  value = "/quote",
  decoders = RequestDecoder.class,
  encoders = JsonEncoder.class)
  public class DirectQuoteSocket {
  @Inject
  private QuoteService quoteService;

  @OnMessage
  public void onMessage(final Session session, final ValueRequest request) {
    final Optional<Quote> quote = quoteService.findByName(request.getName());
      final ValueResponse response = new ValueResponse();
        if (quote.isPresent()) {
            response.setFound(true);
            response.setValue(quote.get().getValue()); // false
        }

        if (session.isOpen()) {
            try {
                session.getBasicRemote().sendObject(response);
                } 
                catch (final EncodeException | IOException e) {
                throw new IllegalArgumentException(e);
            }
        }
    }
}

Provision some data

At this point, we have our application. Now, we need to ensure that it has some data and, then, move on to evaluating its performance.

Without delving too much into the business details, we will implement the provisioning in two passes:

  • Find all the symbols to update
  • For each symbol found, update the price in the database

To do so, we will use two public webservices:

The first one is a plain CSV file, which we will parse without any library to keep things simple and because the format does not require special escaping/parsing. The second one will return a JSON payload, which we can read directly using the JAX-RS 2.1 client API.

Here is how we can retrieve our data:

private String[] getSymbols(final Client client) {
    try (final BufferedReader stream = new BufferedReader(
            new InputStreamReader(
                    client.target(symbolIndex)
                            .request(APPLICATION_OCTET_STREAM_TYPE)
                            .get(InputStream.class),
                    StandardCharsets.UTF_8))) {

        return stream.lines().skip(2/*comment+header*/)
                .map(line -> line.split(","))
                .filter(columns -> columns.length > 2 && !columns[1].isEmpty())
                .map(columns -> columns[1])
                .toArray(String[]::new);
    } catch (final IOException e) {
        throw new IllegalArgumentException("Can't connect to find symbols", e);
    }
}

Note that we directly read a buffered reader backed by the HTTP response stream. Once the symbols are extracted, we can simply iterate over them and request the price of each quote:

try {
    final Data data = client.target(financialData)
            .resolveTemplate("symbol", symbol)
            .request(APPLICATION_JSON_TYPE)
            .get(Data.class);

    if (!data.hasPrice()) {
        LOGGER.warning("Can't retrieve '" + symbol + "'");
        return;
    }

    final double value = data.getQuoteSummary().getResult().get(0)
        .getFinancialData().getCurrentPrice().getRaw();

    final Quote quote = quoteService.mutate(symbol, quoteOrEmpty ->
            quoteOrEmpty.map(q -> {
                q.setValue(value);
                return q;
            }).orElseGet(() -> {
                final Quote newQuote = new Quote();
                newQuote.setName(symbol);
                newQuote.setValue(value);
                quoteService.create(newQuote);
                return newQuote;
            }));

    LOGGER.info("Updated quote '" + quote.getName() + "'");
} catch (final WebApplicationException error) {
    LOGGER.info("Error getting '" + symbol + "': " + error.getMessage()
    + " (HTTP " + (error.getResponse() == null ? "-" :
    error.getResponse().getStatus()) + ")");
}

This piece of code sends an HTTP request, thanks to the JAX-RS client API and JSON-B, which unmarshalls a data model. Then, we use the obtained data to update our database quote if it already exists; otherwise, we use the data to create the database quote.

The code now needs to be wired to be executed. We have multiple options here:

  • Execute it at startup
  • Execute it regularly
  • Execute it when an endpoint is called

In the context of this book, we will use the first two options. The startup is common for us, even if it is not as realistic, because once started, we will get some data. The second option will use an EJB 3.2 @Schedule, which will run hourly.

The startup implementation requires a simple CDI bean with a method calling the previous logic when @ApplicationScoped is created (at startup):

@ApplicationScoped
public class InitialProvisioning {
    @Inject
    private ProvisioningService provisioningService;

    public void onStart(@Observes @Initialized(ApplicationScoped.class) final ServletContext context) {
        provisioningService.refresh();
    }
}

The scheduling is done, thanks to the Enterprise Java Bean @ScheduleAPI, which allows us, in one annotation, to request the container to regularly execute a method:

@Singleton
@Lock(WRITE)
public class DataRefresher {
    @Inject
    private ProvisioningService provisioningService;

    @Schedule(hour = "*", persistent = false, info = "refresh-quotes")
    public void refresh() {
        provisioningService.refresh();
    }
}

Note

In a real application, you will probably want to configure the refresh frequency and use the TimerService API to trigger the execution based on the application configuration. In the same spirit, the startup execution could be ignored based on the configuration in order to have a faster startup.

 

Application summary


When working on the performance, it is always important to keep two things in mind:

  • The application business (what the application does)
  • The application technical stack (how the application was designed)

Even if the information you have about these two points is very high-level, ensure that you know them before working on the performance.

Let's do this exercise with our application and ensure that we know how to answer both the questions.

The application business

Our application is responsible for providing the quote prices to HTTP or WebSocket clients. With its model and customer/quote relationship, it can enable us to provide (or not provide) the price accessed by the customer if we add permissions or rules, for instance. What is important to see at this stage is that both the entities are in a relationship and that our application can visit this relationship for its business needs and trigger an implicit lazy loading of the relationship entities.

The data is injected into the system based on two external HTTP sources (CBOE and Yahoo). The first one provides a symbol dictionary of the quotes, and the second one, the prices.

The application design

Technically, the provisioning of the quote and prices is done asynchronously (not when a customer request is sent). It retrieves the data using a JAX-RS 2.1 client and inserts it as fast as possible into the database.

Access to the application is gained either through HTTP or WebSocket. In both cases, the application uses a JSON format for message exchange.

 

The application server


Java EE defines specifications and, therefore, you can find several different implementations. Each major vendor has its own server but, of course, for us and Java EE, a lot of servers are fully open source. As Java EE 8 is very recent, we will use GlassFish, which is the reference implementation and is therefore the first one to be compliant with the specification (it must be released with the specification). However, there are a lot of alternatives (such as Apache TomEE, Wildfly, Payara, Liberty Profile, and so on), which will probably follow in the coming months.

GlassFish can be downloaded from its website (https://javaee.github.io/glassfish/download). We need the 5.x version to target Java EE 8, but due to its early release, a major part of this book will work with the previous versions.

If you want to integrate it with your development environment (and Maven), you can add the GlassFish repository to pom.xml, as follows:

<pluginRepository>
  <id>maven-java-net</id>
  <url>https://maven.java.net/content/groups/promoted/</url>
</pluginRepository>

Add the GlassFish plugin without forgetting to specify the version of the server in order to override the default one, which is now quite old:

<plugin> <!-- glassfish.version = 5.0 -->
  <groupId>org.glassfish.embedded</groupId>
  <artifactId>maven-embedded-glassfish-plugin</artifactId>
  <version>3.1.2.2</version>
  <configuration>
    <app>target/${project.build.finalName}</app>
    <port>9090</port>
    <contextRoot>${project.artifactId}</contextRoot>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>org.glassfish.main.common</groupId>
      <artifactId>simple-glassfish-api</artifactId>
      <version>${glassfish.version}</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish.main.extras</groupId>
      <artifactId>glassfish-embedded-all</artifactId>
      <version>${glassfish.version}</version>
    </dependency>
  </dependencies>
</plugin>

With this setup, you can run the following command to package the application as war and deploy it in GlassFish:

$ mvn package embedded-glassfish:run

To shut down the server, type X and ENTER.

 

Testing the application


Before starting to work on our application from the performance window, let's get a bit familiar with it. We will not browse and test all the endpoints but just check how to get a price using the JAX-RS layer and WebSocket layer. In other words, we will define two customer use cases of our application.

The goal here is to ensure that we know how to use the application to be able to write test scenarios later. To do so, we will execute some requests manually on both fronts (HTTP and WebSocket).

Get a quote price the JAX-RS way

The endpoint we saw previously has been deployed on /<application_context>/api/quote/{quoteId} with the context of the web application, application_context. If you used the previous setup, it is, most likely, the artifact ID of the Maven project. Let's consider from now on that it is quote-manager.

Here is what it returns for one of the quotes:

$ curl -v http://localhost:9090/quote-manager/api/quote/8
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9090 (#0)
> GET /quote-manager/api/quote/8 HTTP/1.1
> Host: localhost:9090
> User-Agent: curl/7.52.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: Undefined Product Name - define product and version info in config/branding 0.0.0 
< X-Powered-By: Servlet/3.1 JSP/2.3 (Undefined Product Name - define product and version info in config/branding 0.0.0 Java/Oracle Corporation/1.8)
< Content-Type: application/json
< Content-Length: 54
< 
* Curl_http_done: called premature == 0
* Connection #0 to host localhost left intact
{"id":8,"name":"JOBS","customer_count":0,"value":59.4}

This kind of application often needs a kind of index endpoint to be able to browse quotes (in a nice user interface or a command-line interface, for instance). In our case, it is our find all endpoint, which supports pagination through the query parameters. Here is how to use it and the kind of data it returns:

$ curl -v http://localhost:9090/quote-manager/api/quote?from=0&to=5
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9090 (#0)
> GET /quote-manager/api/quote?from=0 HTTP/1.1
> Host: localhost:9090
> User-Agent: curl/7.52.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: Undefined Product Name - define product and version info in config/branding 0.0.0 
< X-Powered-By: Servlet/3.1 JSP/2.3 (Undefined Product Name - define product and version info in config/branding 0.0.0 Java/Oracle Corporation/1.8)
< Content-Type: application/json
< Content-Length: 575
< 
{"total":10,"items":[{"id":1,"name":"FLWS","customer_count":0,"value":9.0},{"id":2,"name":"VNET","customer_count":0,"value":5.19},{"id":3,"name":"XXII","customer_count":0,"value":2.2},{"id":4,"name":"TWOU","customer_count":0,"value":50.1},{"id":5,"name":"DDD","customer_count":0,"value":12.56},{"id":6,"name":"MMM","customer_count":0,"value":204.32},{"id":7,"name":"WBAI","customer_count":0,"value":10.34},{"id":8,"name":"JOBS","customer_count":0,"value":59.4},{"id":9,"name":"WUBA","customer_count":0,"value":62.63},{"id":10,"name":"CAFD","customer_count":0,"value":14.42}]}

Get the price, the WebSocket way

The WebSocket endpoint is deployed on /<application_context>/quote, and some exchanges can look like the following:

connect> ws://localhost:9090/quote-manager/quote
send> {"name":"VNET"}
received< {"found":true,"value":5.19}
send> {"name":"DDD"}
received< {"found":true,"value":12.56}
disconnect>
Connection closed: Close status 1000 (Normal Closure)

What is interesting to see in this communication dump is the fact that the connection lasts for more than one request, and it is based on the symbol more than the identifier (compared to the previous JAX-RS samples).

 

Setting up MySQL


All the previous parts will work transparently in Glassfish, as it can provide you with a default database if none is set since Java EE 7. This default database is an Apache Derby one for Glassfish. Considering that we will work on the performance soon, we want a recent production database. To ensure this, we will set up MySQL.

Assuming that you installed MySQL for your operating system and that it runs on localhost:3306 (the default), we need to create a new database. Let's call it quote_manager:

$ mysql -u root -p
Enter password: ******
...
mysql> create database quote_manager;
Query OK, 1 row affected (0.00 sec)

Now that we have a database, we can configure it in Glassfish and let JPA 2.2 create the tables for us based on our model. For this, we need to create glassfish-resources.xml in the WEB-INF folder of the war package (put it in src/main/webapp/WEB-INF in the Maven project):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN"
    "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
  <jdbc-connection-pool allow-non-component-callers="false"
                        associate-with-thread="false"
                        connection-creation-retry-attempts="0"
                        connection-creation-retry-interval-in-seconds="10"
                        connection-leak-reclaim="false"
                        connection-leak-timeout-in-seconds="0"
                        connection-validation-method="auto-commit"
                        datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
                        fail-all-connections="false"
                        idle-timeout-in-seconds="300"
                        is-connection-validation-required="false"
                        is-isolation-level-guaranteed="true"
                        lazy-connection-association="false"
                        lazy-connection-enlistment="false"
                        match-connections="false"
                        max-connection-usage-count="0"
                        max-pool-size="10"
                        max-wait-time-in-millis="120000"
                        name="MySQLConnectinoPool"
                        non-transactional-connections="false"
                        pool-resize-quantity="2"
                        res-type="javax.sql.DataSource"
                        statement-timeout-in-seconds="-1"
                        steady-pool-size="8"
                        validate-atmost-once-period-in-seconds="0"
                        validation-table-name="DUAL" wrap-jdbc-objects="false">
    <property name="URL" value="jdbc:mysql://localhost:3306/quote_manager"/>
    <property name="User" value="root"/>
    <property name="Password" value="password"/>
  </jdbc-connection-pool>
  <jdbc-resource jndi-name="java:app/jdbc/quote_manager" pool-name="MySQLConnectinoPool" enabled="true"/>
</resources>

Alternatively, you can also do it through code using the @DataSourceDefinition annotation, which is more portable than the specific descriptor of GlassFish (this is the solution we will rely on from now on):

@DataSourceDefinition(
        name = "java:app/jdbc/quote_manager",
        className = "com.mysql.jdbc.Driver",
        url = "jdbc:mysql://localhost:3306/quote_manager",
        user = "root",
        password = "password"
)
public class DataSourceConfiguration {
}

If you recompile and restart the server, you will see that it has created the tables, thanks to our persistence.xml configuration:

mysql> show tables;
+-------------------------+
| Tables_in_quote_manager |
+-------------------------+
| CUSTOMER                |
| QUOTE                   |
| QUOTE_CUSTOMER          |
| SEQUENCE                |
+-------------------------+

If you are waiting for the server to start and have kept the provisioning activated, you will also see some data in the QUOTE table:

mysql> select * from QUOTE limit 10;
+----+-------+-------+
| ID | NAME | VALUE  |
+----+-------+-------+
| 1  | FLWS  | 9     |
| 2  | VNET  | 5.19  |
| 3  | XXII  | 2.2   |
| 4  | TWOU  | 50.1  |
| 5  | DDD   | 12.56 |
| 6  | MMM   | 204.32|
| 7  | WBAI  | 10.34 |
| 8  | JOBS  | 59.4  |
| 9  | WUBA  | 62.63 |
| 10 | CAFD  | 14.42 |
+----+-------+-------+
 

Conclusion


Now we have our functional Quote Manager application, and we can deploy it in a Java EE 8 server (GlassFish here) and store our data in a real database (MySQL).

Till now, we have mainly worked on making the application functional. Thanks to the high-level APIs of Java EE, this was not so hard, but it is important to understand what we used and what the performance implications of each element of our stack are, to be able to validate/invalidate the performance figures once you have them in your hands.

 

Summary


In this chapter, we created an application responsible for managing quote prices and enabling clients to access them through HTTP and WebSockets. The application uses plain Java EE code (no external dependencies). We also saw how to link the application to a database. We used MySQL as the database, which is a free and very common choice.

In the next chapter, we will go deeper into the Java EE stack, and understand its role and what it implies for the application in terms of the application's performance.

About the Author

  • Romain Manni-Bucau

    Romain Manni-Bucau is a senior software engineer who has been working in the Java EE space for 7 years and, more recently, with big data. He joined the Apache EE family in 2011 (TomEE, Johnzon, OpenWebBeans, BatchEE, OpenJPA, and Meecrowave). He mainly contributes to open source projects (CukeSpace, Arquillian, Apache Beam, and others) and aims to make development easy and efficient, letting people create what they have in mind without caring for the technical challenges. You can follow Romain on twitter at @rmannibucau.

    Browse publications by this author

Latest Reviews

(3 reviews total)
Good Good good good good good good
Excellent read, deep subject knowledge
Good book but you need a knowledge of Java ee

Recommended For You

Java EE 8 Development with Eclipse - Third Edition

Develop and deploy fully functional applications and microservices utilising Tomcat, Glassfish servers, Cloud and docker in Java EE 8

By Ram Kulkarni
Java EE 8 Design Patterns and Best Practices

Get the deep insights you need to master efficient architectural design considerations and solve common design problems in your enterprise applications.

By Rhuan Rocha and 1 more
Java EE 8 Microservices

Build microservices-based enterprise applications with the latest version of Java EE

By Kamalmeet Singh and 3 more
Software Architect's Handbook

A comprehensive guide to exploring software architecture concepts and implementing best practices

By Joseph Ingeno