Saturday, September 16, 2023

Azure JBoss EAP On App Service

Over the past couple of months I have had the opportunity to check out the Microsoft Azure JBoss EAP On App Service, and I wanted to share my thoughts on this service.  If you have not previously heard of it, you can think of it as a cloud application service which enables one to deploy Jakarta EE applications in various formats, and host them on Azure.  The Azure App Service contains a plethora of options, from allowing one to deploy a single JAR, to providing the ability to deploy containers, to the ability of deploying single WAR files. 

To get started with the service, you can go to the Azure Portal and create a free account.  There are a number of different services that are available for free to get started and take it for a spin.  If you are interested in using the platform for development and production, you will need to subscribe to a paid plan.  If you wish to get started on a learning path, then there are some great articles available to check out, but here I will show you what I did in order to deploy a simple Jakarta EE application to the cloud using this service.  There are also some excellent examples on Github, including the Jakarta EE Azure example by Reza Rahman.  Let's go quickly through some of my experiences while working through the Jakarta EE Azure example.  

The database configuration when following through the Jakarta EE Azure example was very easy.  I was able to create a hosted PostgreSQL database on Azure almost instantly.  Following along with the example, I also configured the database security to enable access to the Azure services.  Without the step-by-step of a tutorial, some of this configuration may have been a bit complex, as Azure offers a large variety of options for configuration of the services.  

Next, I created a web application using JBoss EAP.  This again provided many options, but many are fairly straight forward if you are familiar with Java web applications.  I chose Java 11 as my runtime stack and JBoss EAP 7 as the web server.  At this point, I was in a position to install an application in WAR file format to the JBoss EAP server.

Following along with the tutorial, one should install the Azure CLI to their machine in order to deploy a local application.  However, I did not want to install anything locally to my machine.  So instead of performing the local CLI installation,  I used the cloud based CLI that is hosted on Azure.  Using this cloud CLI, I was able to clone one of my personal Github repositories, modify it, build, and deploy it to JBoss EAP 7.  This was very nice...having the ability to perform a complete build and deployment in the web browser.  

A very nice feature is the ability to create resource groups that can be used to group application resources together.  This feature allows one to group databases, web applications, and other services together in order to better manage all of the resources that are required for an application.

Overall, Azure JBoss EAP on App Service is a useful tool and is fairly easy to use.  The downside is that the hosting can be quite costly if not using a free package, but I believe that may change at some point in the near future.  Unfortunately, I was unable to dig much deeper because I left my services running for a couple of weeks and used up my credits.  However, I am happy with my experience in the Microsoft Azure Portal with JBoss EAP 7, and I greatly appreciate the opportunity to look at the service.  I look forward to the opportunity to continue my journey with these resources, hopefully free as a developer for testing purposes, in the future.



Sunday, September 10, 2023

Review: OpenJDK Migration for Dummies

Have you been thinking of moving away from the Oracle JDK to OpenJDK?  The book "OpenJDK Migration for Dummies", written by Simon Ritter of Azul, provides direction for those who are looking to make the move.  In many cases, organizations develop Java applications to work on a specific version of the JDK.  Oftentimes, as long as the application continues running without issue, organizations wouild not upgrade the JDK.  As we well know, with all software, as time goes on, the JDK which is in use can age and become out-of-date, exposing security vulnerabilities.  Moreover, if the JDK in use is aging, then there likely have been many enhancements in more recently released JDKs which could provide performance benefits for the deployed application(s).  

We are at a time where older releases of the JDK such as JDK 8 are no longer being supported by Oracle unless commercial support is being paid.  If an organization needs to maintain the use of JDK 8, then it may make sense for the organization to purchase a support agreement.  In other cases, the organization may be able to move to another JDK that will work with their application.  There are many different options available today.  One of those options is OpenJDK, which is freely available for use.  There are a number of organizations that have taken OpenJDK and added features or enhancements, and typically offer a paid support for enhanced protection or even for access to premium features.  

This book nicely covers the arduous process of learning about the different licensing options when using the Oracle JDK, and what the OpenJDK has to offer.  The book makes it easy to understand so that readers to not need to delve into long white papers to read about them.  The book also covers the different points that need to be reviewed while preparing to make a migration to OpenJDK, including making an inventory of all JDK versions are in use within your organization.  There are several key processes outlined in the book that must not be overlooked while performing a migration, such as looking at each of the "optional" features of the JDK that are in-use within your organization to ensure that they are available in the OpenJDK release of your choice.  For instance, JavaFX may not be incorporated within all OpenJDK releases and may be an additional package to incorporate into your migration.

Since the book is written by Simon who works with Azul, he does cover some of the benefits of the Azul Platform Core JDK.  There are many to cover, and the book offers a high level overview of some of the most important features.  It also covers the question of why commercial support may be something of value to your organization.  What if you still need to run JDK 8 in order to make your application work?  In such a case, it makes sense to pay for commercial support.  How about if your organization is looking to have first hand support?  That is another solid reason to purchase commercial support.  The list goes on.

The book "OpenJDK Migration for Dummies" is a great read for anyone looking to migrate to the OpenJDK.  It is a nice and quick read, landing in at under 80 pages, and it covers the process from start to finish.

Monday, July 03, 2023

Cocktails and Code: Margaritas and Jakarta EE

It is summer time, and that means warm weather, swimming pools, and ice cold drinks.  In this post, I will demonstrate how to develop a simple microservices-based application using Jakarta RESTful Web Services to provide the data for the application.  Did you know that there is an easy way to get started with Jakarta EE using the Jakarta EE Starter project?  In this post, I will demonstrate how to utilize the starter.  First, we need to mix up a drink to enjoy while we code!  If you'd prefer to skip the cocktail and code without it, then please skip to the "Time to Code" section below.

Let's Mix a Cocktail - Classic Margarita Recipe

While you could go to a store and purchase a pre-mixed margarita, those mixes are rarely as good as a classic margarita made from scratch.  I prefer my margaritas on-the-rocks (ice cubes), although it is also popular to have a frozen margarita, which is more of an icy concoction similar to a slush.  Whatever your preference, this classic recipe will work.  Let's get mixing!


What you'll need:

2 fluid ounces of blanco tequila (higher quality is better)

1 1/2 fluid ounces of triple sec (higher quality is better)

1 ounce of lime juice (fresh is best)

1 1/2 cup of ice cubes

Coarse salt

Lime

Cocktail Shaker (improvise if you do not have one) or blender for frozen margarita

Margarita glass (any glass will do, but a margarita glass adds to the experience)


Details:

Cut lime into wedges, and then take one wedge and moisten the rim of the glass with the wedge.  Pour coarse salt onto a plate and dip the moistened rim of the glass into the salt.  Add a 1/2 cup of ice to the glass.

Add 1 cup of ice to shaker.  Pour the tequila, triple sec, and lime juice into a shaker.  Shake for 20-25 seconds, until the shaker is frosted on the outside.  Strain the margarita into the glass and garnish with a slice of lime.  If you prefer a frozen margarita, skip the shaker and instead add 2 cups of ice to a blender and then pour tequila, triple sec, and lime juice into the blender and blend until smooth.  If you do not have a cocktail shaker, any closable liquid-tight container may do the trick...so long as you can shake vigorously without the beverage getting all over your code.

Enjoy the refreshing goodness of a classic margarita!




Time to Code - Jakarta EE Starter

Now that we have a cocktail, it is time to code!  In this example, we are going to build a simple web service using Jakarta RESTful Web Services, and deploy to a Payara application server.  While it is easy to deploy to a Docker or Podman container, in this example I will deploy to a standard application server.  

Creating a Simple Web Service

To get started, head over to Eclipse Starter for Jakarta EE and choose your project options from the generator.  In this example, please choose:

Jakarta EE Version: Jakarta EE 10

Jakarta EE Profile: Platform

Java SE Version: Java SE 17

Runtime: Payara

Docker Support: Yes (this way you can experiment with Docker if you wish)

Once the options are selected, click the "Generate" button, and a ZIP file will be downloaded to your device.    Once downloaded and unzipped to your machine, open the README file to learn how to start the project and run it.  At a minimum, you need to do the following:

1) Ensure your JAVA_HOME environment variable is set to the appropriate JDK for the version you selected at the Jakarta EE Starter.  In this case, ensure that your JAVA_HOME is pointing to JDK 17.  On OS X, the following will set the home accordingly:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/<java-home>>

2) Unzip the downloaded project.  Traverse into the jakartaee-hello-world directory within a command prompt or terminal, and execute the following in order to invoke Maven to download dependencies and run the application/service:

./mvnw clean package cargo:run

3) Once the server has been started, navigate to the following page in a web browser:  http//localhost:8080/jakartaee-hello-world/

This minimialistic application will simply output a Jakarta EE logo and some text onto the screen.    To initiate the RESTful web service, visit the following URL:

http//localhost:8080/jakartaee-hello-world/rest/hello

This web service will return the JSON {"hello":"world"} and it does so by invoking a Jakarta REST web service and returning text to the caller.  The RESTful service accepts a parameter identified as “name”, and if any word is passed to this parameter, then the JSON will be modified to {"hello”:<name>}.  To invoke the web service passing the name “Jane”, open a web browser or use a curl command in a terminal open the following URL:

http://localhost:8080/jakartaee-hello-world/rest/hello?name=Jane

A REST web service endpoint class named HelloWorldResource is where the magic happens.    Open the jakartaee-hello-world project within your favorite Integrated Development Environment (IDE) or use a text editor to open the org/eclipse/jakarta/HelloWorldResourc.java file.  The HelloWorldResource class is annotated with  @Path  to indicate the URI path for invoking the REST web service.  There is a single method named hello is contained within the class.  The  @GET  annotation is placed on the method to indicate that the method is responding to HTTP GET requests, and the  @Produces  annotation ensures that the application will produce or return the JSON media type.  The method accepts a QueryParam identified as name, which should be noted as  the ?name portion from the above URL.  In the body of the method, if the name parameter is null or empty, then the word “world” will be assigned to it.  The method returns an object identified as Hello, which is a plain old Java object (POJO).  If you wish to compile and deploy this application to a stand alone server, then you can do that as well!  Here is a look at the HelloWorldResource REST service class:


public class HelloWorldResource {

@GET

@Produces({ MediaType.APPLICATION_JSON })

public Hello hello(@QueryParam("name") String name) {

if ((name == null) || name.trim().isEmpty())  {

name = "world";

}

return new Hello(name);

}

          }

Code for the Hello object:

public class Hello {

private String name;


public Hello(String name) {

this.name = name;

}

public String getHello(){

return name;

}

}


There you have it! Now you should be able to get up and running quickly with the Jakarta EE Starter. Thanks to the team involved with working on the starter project!  

Thursday, September 22, 2022

JakartaOne Livestream 2022

 The JakartaOne Livestream 2022 will be held virtually on December 6th.  The Call for Papers is now open!  

Please submit your paper to speak at an event that has been very well attended for the last three years!

Submit a Talk!



Sunday, August 15, 2021

Apache NetBeans 12.5

Apache NetBeans 12.5 - beta 1 is available for testing.  Please download it and give it a try at:

https://ci-builds.apache.org/job/Netbeans/job/netbeans-TLP/job/netbeans/job/release125/lastSuccessfulBuild/artifact/dist/netbeans/

New features in this release are listed at the following link:  https://cwiki.apache.org/confluence/display/NETBEANS/Apache+NetBeans+12.5






Saturday, October 24, 2020

Kicking the Tires of Jakarta EE 9 with Payara

We've been hearing all about it.  Jakarta EE 9 will be released in November 2020.  The Jakarta EE working group and community have been working hard to bring this release to the masses, and it is about ready.  Here's how to take a look at it today, using one of the most popular application server containers.

1.  Download the latest Payara Server Community 5.2020.5 release, which includes "Tech Preview" support for Jakarta EE 9.  https://www.payara.fish/downloads/#community

2.  Create a simple service using Jakarta EE APIs, adding the jakarta.platform:jakartaee.jakarta.jakartaee-api:9.0.0-RC3 dependency. 

3.  Build and deploy the service to Payara Server Community 5.2020.5.

That's it.  The Payara Server Community edition includes an Eclipse Transformer Configuration Option, which will automatically detect jakarta.* package references during the deployment of the application, and transform the class and resource files from the jakarta.* namespace to javax.*.  Take a look at this Payara blog post for more info:  https://blog.payara.fish/eclipse-transformer-configuration

Example POM including Payara Server Community Support:


<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>com.employeeevent</groupId>
    <artifactId>AcmeChat</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>AcmeChat-1.0-SNAPSHOT</name>
    
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <failOnMissingWebXml>false</failOnMissingWebXml>
        <jakartaee>9.0.0-RC3</jakartaee>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>${jakartaee}</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>jakarta.platform</groupId>
                                    <artifactId>jakarta.jakartaee-api</artifactId>
                                    <version>${jakartaee}</version>
                                    <type>pom</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Tuesday, September 08, 2020

Developing a Simple Service with Spring Boot

In this post, I will demonstrate how to create a simple web service using Spring Boot.  This framework makes it almost effortless to develop web services, so long as the appropriate dependencies are in-place.  In this example, I will create a web service that will read the current temperature from a file and make it available to clients via a RESTful endpoint.

The Spring Initializr helps one to choose the dependencies that are required for production a particular solution.  The Initializr can be found at: https://start.spring.io/  

The first thing to note when creating a project using the Initializr is that one can develop Spring Boot applications using a number of different JVM languages, including Java, Kotlin, or Groovy.  This is one area where Spring Boot differs from Jakarta EE, which is primarily focused on the Java language.  One also has the option to choose either Gradle or Maven for their Spring Boot project.  Perhaps one of the most tedious parts of using the Initialzr to create a project is choosing the appropriate dependencies for the project.  The reason being that there are so many dependency options, it can take a while to learn which are the most useful for a particular solution.  This is not a bad thing...it is just something that needs to be learned over time.

To get started, choose the following options within the Initializr:

Project: Maven

Language: Java

Spring Boot: 2.3.3

Dependencies:  Spring Web

Project Metadata

- Group:  org.demo

- Artifact: poolservice

- Name: poolservice

- Package Name:  org.demo.poolservice

- Packaging:  WAR

- Java:  11

Once these options are chosen, click "Generate" to download the project files.  Once downloaded, open the project in your favorite IDE.  In this case, I will be using Apache NetBeans 12.  The project can now be built and deployed to a container, such as Payara server.  The project comes configured and ready to begin adding RESTful services.  The following code shows the generated PoolserviceApplication class, which was created by the Initializr, contains the @SpringBootApplication annotation.  

package org.demo.poolservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PoolserviceApplication {

	public static void main(String[] args) {
		SpringApplication.run(PoolserviceApplication.class, args);
	}

}

The @SpringBootApplication annotation is a shortcut annotation that combines the default functionality of the following three annotations:

  • @EnableAutoConfiguration: enables Spring Boot auto-configuration
  • @ComponentScan: enables component scanning on the package where application is loaded
  • @Configuration: allows registration of extra beans in the context or the ability to import additional configuration classes

Since the application is configured by default, generate a new RESTful service by creating a class named HelloController in the same package and placing the following code into it:

package org.demo.poolservice;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
public class HelloController {

	@RequestMapping("/")
	public String index() {
		return "Greetings from Spring Boot This is the main service!";
	}

}

The @RestController annotation wires the class up as a RESTful web service controller by combining the traditional @Controller and @ResponseBody annotation functionality. In this example, I am using the request root to serve the response, as the @RequestMapping annotation indicates. Once this controller class has been added and the code has been compiled and redeployed, the URL http://localhost:8080/poolservice can be visited to display the message: "Greetings from Spring Boot This is the main service!".

It is time to add the functionality for reading the current temperature from a text file (written by a Raspberry Pi) by creating a new class named TemperatureReader. This class can be made a contextual bean by annotating it with @Component and specifying a name by which the bean will be referenced.  In this case, "temperatureReader".  The functionality of the class is very simple, as it reads temperatures from a file that are in the format: (23.5, 78.3), and makes them accessible via currentTemperatureC and currentTemperatureF, respectively.

package org.demo.poolservice;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.springframework.stereotype.Component;


@Component("temperatureReader")
public class TemperatureReader {
    
    private String currentTemperatureF;
    
    private String currentTemperatureC;
    
    protected String readTemperatureFile() {
        
        
        String temperatureFile = "/<>/temperature.txt";
        System.out.println("Temperature File: " + temperatureFile);
        java.nio.file.Path path = Paths.get(temperatureFile);
        String currentTemperature = null;
        try (BufferedReader reader = Files.newBufferedReader(path, Charset.forName("UTF-8"))) {

            String currentLine = null;
            while ((currentLine = reader.readLine()) != null) {//while there is content on the current line
                currentTemperature = currentLine;
            }
        } catch (IOException ex) {
            ex.printStackTrace(); //handle an exception here
        }
        return currentTemperature;
    }

    /**
     * @return the currentTemperatureF
     */
    public String getCurrentTemperatureF() {
        String temp = readTemperatureFile();
        currentTemperatureF = temp.substring(temp.indexOf(",") + 1, temp.lastIndexOf(")"));
        return currentTemperatureF;
    }

    /**
     * @param currentTemperatureF the currentTemperatureF to set
     */
    public void setCurrentTemperatureF(String currentTemperatureF) {
        this.currentTemperatureF = currentTemperatureF;
    }

    /**
     * @return the currentTemperatureC
     */
    public String getCurrentTemperatureC() {
        String temp = readTemperatureFile();
        currentTemperatureC = temp.substring(temp.indexOf("(") + 1, temp.lastIndexOf(","));
        return currentTemperatureC;
    }

    /**
     * @param currentTemperatureC the currentTemperatureC to set
     */
    public void setCurrentTemperatureC(String currentTemperatureC) {
        this.currentTemperatureC = currentTemperatureC;
    }
    
}

Finally, to make the temperature readings available via a RESTFul service, create a controller named TemperatureController and annotate it with @RestController. Inject the TemperatureReader by annotating a private TemperatureReader field with @Autowired. The bean can then be used to obtain the temperature via the use of the TemperatureReader field. The URL mapping for the service is supplied via the @RequestMapping("/temperature") annotation on the index() method, which will be used to serve up the temperature at the respective request mapping. The functionality contained within the index() method is very minimal, as it merely calls upon the temperatureReader.getCurrentTemperatureF() method to return the current temperature in Fahrenheit.

package org.demo.poolservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TemperatureController {
    
    @Autowired
    private TemperatureReader temperatureReader;
    
    @RequestMapping("/temperature")
	public String index() {
            return temperatureReader.getCurrentTemperatureF();
	}
    
}
To see the temperature, visit the URL: http://localhost:8080/poolservice/temperature

Sunday, August 23, 2020

Developing Web & Enterprise Applications for the JVM - "This and That"

I am starting a new series of blog posts which will cover the ins and outs, tips and tricks of developing web and enterprise applications with Java.  Some people refer to enterprise meaning that an application requires interaction with a database and/or communication with other applications and services.  Therefore, this blog series will apply to "Web" or "Enterprise" application development for the Java Virtual Machine (JVM) using a number of different technologies.

In this blog series, I will not focus on any single strategy or API, but rather, I will cover a number of technical strategies and options that can be used for developing applications for deployment on the JVM.  The main goal is to provide readers with a broad overview of the ecosystem available for the JVM, ensuring that one knows various paths that can be followed to develop sophisticated applications.  I also hope to keep each post short and to the point, to allow one more time to develop and experiment, rather than reading my ramblings.  

There is no doubt that the JVM is one of the key technologies of our time, as it is installed on millions of devices worldwide, and tens of thousands of applications have been written for the JVM.  Even though it is one of the most popular platforms for application development, there tends to be questioning on which path to take when developing certain applications.  I like to think of the myriad of options as "tools" that are in the toolshed.  We can utilize a Phillips screwdriver to tighten an Allen wrench screw, but it certainly may not be the optimal choice and there are more suitable options available.  Much in the same manner, one may use Jakarta EE to develop a microservice just because he or she may know that tech, but there may be better options out there for the particular solution at hand.

Hopefully this blog series will provide you with a better understanding of which tool to choose for your future projects.  Just because you know one technology very well, it does not mean that you cannot pick up another technology easily enough and make your toolshed even more substantial for future endeavors.  One should continually learn new technologies so that they are not locked into using a single strategy.

To that end...I will start this series by showing how to develop a very simple web service which will be used to provide a temperature reading.  The web service end point can be harnessed by clients to obtain the current temperature by reading a file that has been written by a temperature sensor connected to a raspberry pi.  In the first post, I will utilize Jakarta EE 8 to build this service.  

Note:  As you read on in this series, you will see that version numbers of APIs mean the world.  As with any technology, web and enterprise development are ever-changing, and so moving from one version to the next usually means that there may be a number of changes in the way a service or application needs to be developed.  

The following index will be updated to include posts as they are written for inclusion in this series.


Developing a Simple Web Service with Jakarta EE 8

For context, Jakarta EE 8 is very much the same as Java EE 6 through Java EE 8.  The main differences between Java EE 6 and Jakarta EE 8 are the inclusion of newer specifications and updated technologies that had been added over the years.  Moreover, Jakarta EE 8 is an open source platform, whereas Java EE was not open source.  When Oracle open sourced Java EE to the Eclipse Foundation, it became known as Jakarta EE.

This example application (web service) will create a WAR file which can be deployed to any Jakarta EE 8-compliant application server container.  This includes micro-containers such as Payara Micro or WildFly.  To begin, create a Maven based WAR file project in your favorite Integrated Development Environment (IDE).  The most prevalent IDE's for developing Jakarta EE 8 applications include Eclipse, IntelliJ, or Apache NetBeans.  Another option is to download the Jakarta EE starter project.  In this post, I start with the Jakarta EE starter project and then I will open and develop within Apache NetBeans 12.  I will deploy to Payara Micro.

To get started, grab a copy of the Jakarta EE starter project simple-hello and copy it into a working directory on your machine.  Open the project in your IDE and rename it to PoolService-1.0-SNAPSHOT, as seen in Figure 1.
Figure 1:  Rename Project

Modify the class named org.demo.simple.ApplicationConfig, by adding a path. Once finished, it should look as follows:

package com.demo.simple;

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

/**
 * Configures the Jakarta REST application.
 * 
 * @author Ivar Grimstad (ivar.grimstad@eclipse-foundation.org)
 */
@ApplicationPath("resources")
public class ApplicationConfig extends Application {
    
}
This will create a root path for your Jakarta RESTful web services at the path "resources", meaning that in order to access any JAX-RS resources, preface the service name with this path.

Next, develop a controller class that can be utilized for reading the temperature file when invoked.  Create a new Java class named org.demo.simple.TemperatureController, which is responsible for reading the file and setting values within variables.  The temperature will be in the format (23.5, 74.3) with the first reading being in Celsius, and the second reading being in Fahrenheit. 

package com.demo.simple;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

/**
 *
 * @author juneau
 */
@Named
@RequestScoped
public class TemperatureController {
    
    private String currentTemperatureF;
    
    private String currentTemperatureC;
    
    public TemperatureController(){
        
    }
    
    protected String readTemperatureFile() {
        
        String temperatureFile = "<<path-to-file>>/temperature.txt";
        java.nio.file.Path path = Paths.get(temperatureFile);
        String currentTemperature = null;
        try (BufferedReader reader = Files.newBufferedReader(path, Charset.forName("UTF-8"))) {

            String currentLine = null;
            while ((currentLine = reader.readLine()) != null) {//while there is content on the current line
                currentTemperature = currentLine;
            }
        } catch (IOException ex) {
            ex.printStackTrace(); //handle an exception here
        }
        return currentTemperature;
    }

    /**
     * @return the currentTemperatureF
     */
    public String getCurrentTemperatureF() {
        String temp = readTemperatureFile();
        currentTemperatureF = temp.substring(temp.indexOf(",") + 1, temp.lastIndexOf(")"));
        return currentTemperatureF;
    }

    /**
     * @param currentTemperatureF the currentTemperatureF to set
     */
    public void setCurrentTemperatureF(String currentTemperatureF) {
        this.currentTemperatureF = currentTemperatureF;
    }

    /**
     * @return the currentTemperatureC
     */
    public String getCurrentTemperatureC() {
        String temp = readTemperatureFile();
        currentTemperatureC = temp.substring(temp.indexOf("(") + 1, temp.lastIndexOf(","));
        return currentTemperatureC;
    }

    /**
     * @param currentTemperatureC the currentTemperatureC to set
     */
    public void setCurrentTemperatureC(String currentTemperatureC) {
        this.currentTemperatureC = currentTemperatureC;
    }
    
}

An interested service can now inject TemperatureController using Contexts and Dependency Injection (CDI), and call upon getCurrentTemperatureF() or getCurrentTemperatureC() in order to obtain the requested temperature format.  As such, lastly create a Jakarta Restful Web Services file named TemperatureResource, and enter the following code:

package com.demo.simple;

import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.core.MediaType;

/**
 * JAX-RS Web Service
 *
 * @author juneau
 */
@Path("temperature")
@RequestScoped
public class TemperatureResource {
    
    @Inject
    private TemperatureController temperatureController;

    /**
     * Creates a new instance of TemperatureResource
     */
    public TemperatureResource() {
    }

    /**
     * Calls upon the TemperatureController and obtains the current temperature
     * in Fahrenheit.
     *
     * @return an instance of java.lang.String
     */
    @GET
    @Produces({MediaType.TEXT_PLAIN})
    public String getXml() {
        String currentTemperature = temperatureController.getCurrentTemperatureF();
        return currentTemperature;
    }

    /**
     * PUT method for updating or creating an instance of TemperatureResource
     *
     * @param content representation for the resource
     */
    @PUT
    @Consumes(MediaType.APPLICATION_XML)
    public void putXml(String content) {
    }
    
    /**
     * Calls upon the TemperatureController and retrieves the current temperature
     * in Fahrenheit.  Same as getXml();
     * @return 
     */
    @GET
    @Produces({MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON})
    @Path("f")
    public String getF() {
        String currentTemperature = temperatureController.getCurrentTemperatureF();
        return currentTemperature;
    }

    /**
     * Calls upon the TemperatureController and retrieves the current temperature
     * in Celsius.
     * @return 
     */
    @GET
    @Produces({MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON})
    @Path("c")
    public String getC() {
        String currentTemperature = temperatureController.getCurrentTemperatureC();
        return currentTemperature;
    }
    
}

This is a web service that will be made available at the URI /resources/temperature, so when the URL http://localhost:8080/poolService/resources/temperature is entered, then the temperature in Fahrenheit should be displayed. This happens because the method named getXml() is called by default since it is annotated with @GET and does not contain a path. It will return plain text since it is annotated with @Produces({MediaType.TEXT_PLAIN}). If I wish to change the default format to something else, say XML, then I could modify the @Produces annotation to: @Produces({MediaType.APPLICATION_JSON}).  The getXml() method invokes the TemperatureController getCurrentTemperatureF() method to read the temperature and return the result.

Use your favorite IDE or the command line to compile and build the project to create a WAR (Web Archive) file named simple-hello.war.
 

Deployment

I am going to use a micro container to deploy the application.  However, it could also be deployed into any other Jakarta EE compatible container such as GlassFish, Payara, or Open Liberty.  See the following link for compatible implementations:  https://jakarta.ee/compatibility/

In this case, download Payara Micro 5.2020.4 and place the downloaded JAR file into a directory.  Next, open a terminal and type in the following to start Payara Micro and deploy the WAR file:

java -jar payara-micro-5.2020.4.jar --deploy <path-to-war-file>/simple-hello.war

This will start the server and output something similar to the following in the terminal:
Payara Micro URLs:
http://<your-machine>:8080/simple-hello

'simple-hello' REST Endpoints:
GET	/simple-hello/resources/application.wadl
GET	/simple-hello/resources/hello
GET	/simple-hello/resources/temperature
PUT	/simple-hello/resources/temperature
GET	/simple-hello/resources/temperature/c
GET	/simple-hello/resources/temperature/f

]]
Input the following URL into the browser to run the service:  http://<your-machine>:8080/simple-hello/resources/temperature

Thursday, November 28, 2019

Jakarta EE Contribution - Getting Started

Are you interested in helping out with moving Jakarta EE forward?  So am I, and I wanted to provide a few details that can be used to help those who are interested in getting started.

Step 1:

The first step to begin contributing is to sign the Eclipse Foundation Committer and Contributor Agreement (ECA):  https://www.eclipse.org/legal/committer_process/re-sign/. The ECA must be signed by anyone who wants to contribute to an Eclipse open source software or specification project.  This allows one to have the ability to accept a contributor's pull requests.  

The Eclipse ICA (Eclipse Individual Committer Agreement) can be signed by those who do not work for the Eclipse Foundation Member Company when they become a committer.

** Note:  Be sure that your employer is okay with you signing the agreement.  In many cases it is a win-win for all...as your contributions will benefit the community, including your employer.

Step 2:

Sign up for the mailing lists and get involved.  Certainly sign up for the Jakarta Platform Developer mailing list, as many of the details for moving the platform forward are being discussed here.


Look through the list of projects that are part of Jakarta EE and sign up for the individual mailing lists for those projects which you are interested in helping out: https://projects.eclipse.org/projects/ee4j


A few of the tasks for the delivery of Jakarta EE 9 include the following:

* Take control of the CI/CD pipeline for release.
* Coordinate release across all the projects.
* Make the actual changes to the API, docs and TCK.
* Make the implementation changes on at least one server - probably Eclipse GlassFish.


Hopefully this will be of use to hose who are interested in getting started.  

Monday, November 26, 2018

Automating Docker Deployments Using Jib and Payara Micro

If you are like me, you spend most of your day developing applications.  During that time, it is difficult to carve out the time to manage containers.  Writing a Dockerfile has almost become part of the normal development process nowadays, but there is a different way.  I'd rather spend as much time developing my applications as possible, and leave the Docker configuration as a minimal deployment procedure that is automatically completed for me.

I believe that the folks at Google were thinking in this way when they produced Jib.  The Jib project allows one to automate the creation of a Docker image so that the container can simply be started up after building the project.  There is no need to create a Dockerfile, as Jib takes care of this for you.  Jib works with both Maven and Gradle, and in this post I will demonstrate how to use it with Maven and deploy to Payara Micro.  By default, Jib includes a Jetty distribution to which an application will deployed inside of the container.  However, I am familiar with Payara Micro and quite enjoy the Payara Micro environment, so I'll bypass the use of Jetty and deploy to Payara Micro in this example.

To begin, it is important to get familiar with the Jib Maven process by reading through the documentation.

In this example I am utilizing the SportsTeamQueryService microservice that I produced for a Java Magazine article in 2018, but simply modifying the POM file to incorporate the Jib build.  I will not go into full detail on Jib in this post, as the documentation itself does quite well.  Rather, I will explain how I was able to get this project to build and deploy to Docker on my Mac.  If using a different OS, you may have to configure host-container networking in order for the container to communicate with the host database.

To begin, the POM file looks like the following, including only those dependencies that are required for the project.  I call your attention to the <plugins> section for the Jib hooks:

<?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>org.javamagazine</groupId>
    <artifactId>SportsTeamQueryService</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>

    <name>SportsTeamQueryService</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>javax.persistence-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa</artifactId>
            <version>2.5.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.core</artifactId>
            <version>2.5.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.ejb</groupId>
            <artifactId>javax.ejb-api</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.asm</artifactId>
            <version>2.5.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.antlr</artifactId>
            <version>2.5.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.jpql</artifactId>
            <version>2.5.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>javax.persistence</artifactId>
            <version>2.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
            <version>2.5.2</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <!-- Docker JIB Dependency-->
            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>0.10.0</version>
                <configuration>
                    <from>
                        <image>
                            payara/micro:latest
                        </image>
                    </from>
                    <to>
                        <image>sportsteamqueryservice</image>
                    </to>
                    <container>
                        <appRoot>/opt/payara/SportsTeamQueryService</appRoot>
                        <args>
                            <arg>
                                --addlibs
                            </arg>
                            <arg>
                                /jars/derbyclient.jar
                            </arg>
                            <arg>
                                --deploy
                            </arg>
                            <arg>
                                /opt/payara/SportsTeamQueryService
                            </arg>
                        </args>
                        <environment>                            
                            <DOCKER_HOST>docker.for.mac.localhost</DOCKER_HOST>
                            <DB_NAME>ACME</DB_NAME>
                            <DB_USER>acmeuser</DB_USER>
                            <DB_PASSWORD>password</DB_PASSWORD>
                        </environment>
                    </container>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
</project>


In the POM, I added the Jib workflow within the <plugins> section.  In this project I utilized the latest Jib dependency at the time of writing, using the following maven coordinates:

<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>0.10.0</version>

The <configuration> portion of the plugin specifies custom configuration for my project.  If I leave the <configuration> section off the declaration, then the WAR file generated by the project will be deployed to a Docker image containing a Jetty server.  The WAR file will be available at the Jetty ROOT.  Instead, since I am interested in using Payara Micro, I customized the Jib declaration as follows:

1)  Inherit FROM the latest hosted Payara Micro image using the <from><image> tags.

2)  Next, the <to><image> tags declare the name of the resulting Docker image.

3)  Add a <container> section to specify details about the container.
    a)  First, I added an <appRoot> tag to specify that the WAR file will be loaded into the "/opt/payara/" location, within the "SportsTeamQueryService" directory.

    b)  Next, I added a number of arguments by specifying <args><arg></arg></args>.

The first argument adds the Apache Derby Jar file to the deployment.  One note is that in the project itself, there is a "src/main/jib/jars" folder.  You must add the derbyclient.jar file inside of this "jib/jars" folder so that it is copied into the image.  By default, Jib adds anything within the "src/main/jib" folder into the project image.

The second argument specifies the Payara Micro "--deploy" argument and points to the "/opt/payara/SportsTeamQueryService" directory...which is the exploded WAR for the project.

    c)  Lastly, add the <environment> section to specify any environment variables that will be passed into the image.  In this case, I added database-specific environment variables.  If you take a look at the glassfish-resources.xml file within the project, you can see that these environment variables are referenced to configure the datasource.

<?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 ..abbreviated for brevity...>
        <property name="serverName" value="${ENV=DOCKER_HOST}"/>
        <property name="portNumber" value="1527"/>
        <property name="databaseName" value="${ENV=DB_NAME}"/>
        <property name="User" value="${ENV=DB_USER}"/>
        <property name="Password" value="${ENV=DB_PASSWORD}"/>
        <property name="URL" value="jdbc:derby://${ENV=DOCKER_HOST}:1527/acme"/>
        <property name="driverClass" value="org.apache.derby.jdbc.ClientDriver"/>
    </jdbc-connection-pool>
    <jdbc-resource enabled="true" jndi-name="java:app/jdbc/acme" object-type="user" pool-name="derby_net_acme_acmeuserPool"/>
</resources>

Note that you may need to modify the servername environment variable if not using Docker for Mac.

That is it for configuring Jib!  Now all that is required is simply to build the project and deploy the image to your local Docker repository.  In this case, I open the terminal and traverse inside of the SportsTeamQueryService-Jib project directory and issue the following Maven build:

mvn clean install

After the project has been built, invoke the Jib build by issuing the following:

mvn compile jib:dockerBuild

The Docker image should now be ready to start within the local Docker environment.  To start the container, issue the following:

docker run -d -p 8080:8080 sportsteamqueryservice

The container should now be up and running.  Visit the URL http://localhost:8080/SportsTeamQueryService/rest/teamrosterqueryservice/findAll to see the list of players. 

In order to rebuild the Docker image, simply issue the command:

mvn compile jib:dockerBuild

Project Sources:  https://github.com/juneau001/SportsTeamQueryService-Jib