Coming Up for Air

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.

You can find the source for the whole project here, and for this part here.

Search

    Quotes

    Sample quote

    Quote source

    About

    My name is Jason Lee. I am a software developer living in the middle of Oklahoma. I’ve been a professional developer since 1997, using a variety of languages, including Java, Javascript, PHP, Python, Delphi, and even a bit of C#. I currently work for Red Hat on the WildFly/EAP team, where, among other things, I maintain integrations for some MicroProfile specs, OpenTelemetry, Micrometer, Jakarta Faces, and Bean Validation. (Full resume here. LinkedIn profile)

    I am the president of the Oklahoma City JUG, and an occasional speaker at the JUG and a variety of technical conferences.

    On the personal side, I’m active in my church, and enjoy bass guitar, running, fishing, and a variety of martial arts. I’m also married to a beautiful woman, and have two boys, who, thankfully, look like their mother.

    My Links

    Publications