Getting Started with Eclipse MicroProfile, Part 2: Payara Micro
Monday, October 15, 2018 |Payara Micro is a MicroProfile implementation from the good folks at Payara, based on Payara Server, which is itself based on GlassFish. Whew! If you’re familiar with either GlassFish or Payara, you should feel right at home with Payara Micro.
To start, we need to understand how Payara Micro deploys the application. Payara Micro spins up an instance, albeit a somewhat stripped down version, of Payara Server. Once the server instance has started, Payara Micro deploys your MP application as a web application. In your build file (and we’ll be using Maven), you must declare that you are building a war file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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 3
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>mp-demo-master</artifactId>
<groupId>com.steeplesoft.microprofile</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>payara-micro</artifactId>
<packaging>war</packaging>
<name>payara-micro</name>
<!-- ... -->
Next, we need to declare the dependencies for Payara Micro, but we also need to declare a dependency on our common module, as that’s where the actual application lives. The Payara Micro module here has no real application data in. :)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies>
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>2.0.1</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>fish.payara.extras</groupId>
<artifactId>payara-micro</artifactId>
<version>${version.payara}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
</dependency>
Next, we can add the Payara Micro Maven Plugin to build, which will allow us to start and stop the server using Maven. We will also add some configuration for the Maven Dependency Plugin to copy the Payara Micro jar to the build output directory so that we can start and stop the server from the commandline:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<build>
<plugins>
<plugin>
<groupId>fish.payara.maven.plugins</groupId>
<artifactId>payara-micro-maven-plugin</artifactId>
<version>1.0.2</version>
<configuration>
<payaraVersion>${version.payara}</payaraVersion>
<autoDeployArtifact>true</autoDeployArtifact>
<deployArtifacts>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>war</type>
</artifactItem>
</deployArtifacts>
<daemon>true</daemon>
<deployWar>true</deployWar>
</configuration>
</plugin>
</plugins>
</build>
This will configure the plugin for both the bundle and start goals. That lets us create an uberjar:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ mvn payara-micro:bundle
...
$ java -jar tart/payara-micro-microbundle.jar
...
[2018-10-15T08:47:25.788-0500] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1539611245788] [levelValue: 800]
Payara Micro URLs:
http://jdlee:8080/payara-micro-1.0-SNAPSHOT
http://jdlee:8080/
'payara-micro-1.0-SNAPSHOT' REST Endpoints:
GET /payara-micro-1.0-SNAPSHOT/
GET /payara-micro-1.0-SNAPSHOT/application.wadl
'ROOT' REST Endpoints:
GET /
GET /application.wadl
GET /openapi/
GET /openapi/application.wadl
[2018-10-15T08:47:25.789-0500] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1539611245789] [levelValue: 800] Payara Micro 5.183 #badassmicrofish (build 380) ready in 16,311 (ms)
And we can manually test our app:
1
2
3
4
# curl http://localhost:8080
Hello, world
# curl http://localhost:8080/?name=Payara+Micro
Hello, Payara Micro
I said we can test manually, but we can also write automated tests using Arquillian. We start by adding the dependencies for our tests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.4.0.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-bom</artifactId>
<version>3.1.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-api-maven</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-impl-maven</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>fish.payara.arquillian</groupId>
<artifactId>arquillian-payara-micro-5-managed</artifactId>
<version>1.0.Beta3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>fish.payara.extras</groupId>
<artifactId>payara-embedded-all</artifactId>
<version>${version.payara}</version>
<scope>test</scope>
</dependency>
</dependencies>
Let’s start with a simple test. This test will run in-container, and will demonstrate that the injection is working, and… that the methods can return Strings. :P
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@RunWith(Arquillian.class)
public class InjectionTest {
@Inject
private HelloWorldService service;
@Inject
private HelloWorldResource resource;
@Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class)
.addPackage(HelloWorldService.class.getPackage())
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
@Test
public void verifyInjection() {
assert service != null;
assert resource != null;
}
@Test
public void serviceSaysHelloCorrectly() {
assert "Hello, Test".equals(service.sayHello("Test"));
}
@Test
public void resourceSaysHelloCorrectly() {
assert "Hello, Test".equals(resource.sayHello("Test"));
}
}
That’s all there is to it. For using Payara Micro as an Arquillian container, there is no need, at least in the most basic
of usages, for arquillian.xml
.
To run this test from IDEA, there seems to be a bit of extra work needed. It seems that Payara Micro depends on the
environment variable MICRO_JAR
to tell the bootstrapping code where to find the JAR. It’s on the classpath, but that
doesn’t seem sufficient (I can, of course, be way off base — I’m not an expert on Payara Micro or Arquillian), so here
are the changes to my POM that I needed to make things work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>fish.payara.extras</groupId>
<artifactId>payara-micro</artifactId>
<version>${version.payara}</version>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/</outputDirectory>
<destFileName>payara-micro.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<environmentVariables>
<MICRO_JAR>${project.build.directory}/payara-micro.jar</MICRO_JAR>
</environmentVariables>
</configuration>
</plugin>
It also seemed that I needed to run the tests from the command-line first to make sure the JAR file was in place, as it seems that IDEA does not run the usual Maven lifecycle prior to running the tests. Again, I’m no expert, so if someone who is can clear up the confusion, I would greatly appreciate it.
Finally, one last test, which will exercise our REST endpoint end-to-end:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@RunWith(Arquillian.class)
@RunAsClient
public class HelloWorldResourceTest {
@ArquillianResource
private URL deploymentURL;
@Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class)
.addPackage(HelloWorldService.class.getPackage())
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
@Test
public void shouldSayWorld() throws URISyntaxException, IOException {
requestAndTest(deploymentURL.toURI(), "Hello, world");
}
@Test
public void shouldSayPayara() throws URISyntaxException, IOException {
requestAndTest(new URIBuilder(deploymentURL.toURI())
.setParameter("name", "Payara").build(),
"Hello, Payara");
}
private void requestAndTest(URI uri, String s) throws IOException {
try (CloseableHttpResponse response = HttpClients.createMinimal().execute(new HttpGet(uri))) {
assert EntityUtils.toString(response.getEntity()).equals(s);
}
}
}
We tell JUnit to run the test with Arquillian
, and that we want to run the tests on the client. Ordinarily, Arquillian
magically wraps up your tests, ships them to the server, and runs them there. For this test, we don’t want that, thus
@RunAsClient
. Using the Apache HttpClient from HttpComponents, we make an "out of process" REST request to the endpoint
and verify the responses.
And there you have a very basic Payara Micro example, complete with working Arquillian tests. The thing to take away from this is how simple it is to wrap your application in a Payara Micro runtime: the only additional work was configuring your build to output the uberjar. That’s awesome, as that means there’s no real application glue required for the specific runtime environment. We’ll see that this mostly holds true across the other MicroProfile, thus demonstrating the power of standards and portability.
In the next installment, we’ll take a look at Thorntail.