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!
I am an application developer, database administrator, technical writer, and Java evangelist. Frequent this blog to learn from my experiences in Java, JavaEE, PL/SQL, and Python/Jython development. Follow my tweets @ http://twitter.com/javajuneau
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!
Apache NetBeans 12.5 - beta 1 is available for testing. Please download it and give it a try at:
New features in this release are listed at the following link: https://cwiki.apache.org/confluence/display/NETBEANS/Apache+NetBeans+12.5
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>
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:
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
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.
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.
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.
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; } }
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.
java -jar payara-micro-5.2020.4.jar --deploy <path-to-war-file>/simple-hello.war
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 ]]