The Tribe Blog

Java in the Cloud: EEruption of the Heroku

3571
No Comments

Today all major vendors have their own Cloud platform and most of them are betting on it for the future. Even if the cloud is not yet the everyday solution, it brings new challenges for servers. This post will deal with one example to show how Apache TomEE meets the challenge with success.

What are the challenges of the Cloud?

All Cloud platforms are a bit different as each have different default targets (i.e., Spring, Java EE, your own platform, only runtime, only build, or even build + runtime, etc.), however, they all share some common points a modern framework/server needs to tackle:

  • Injection of Resource configuration through the environment: For Java it would be very simple to get to the system property. But these Cloud platforms generally support more than Java (e.g., JavaScript, Ruby, Perl, etc.), so they use the same common point of all languages, i.e., the environment.
  • Packaging: Here the differences become huge! Some platforms accept standard binary (as for TomEE, a WAR file), some needs an executable JAR file, while others directly accept source code. This is surely the most vendor specific part of the cloud solution. What is important to keep in mind is that either your platform supports your target (for example, if you want to deploy on TomEE; it provides you with an instance of TomEE and you just need to copy your WAR file) or you need to do it yourself. In the latter case you have two main subcases: either you can start your container by configuration and deploy your application in it, or you need to provide a Java command for the platform to launch. This can be an executable JAR file or just a main (String[]); depending on the solution you choose.
  • Configuration: In the case of Java you need a few, but important configurations, to ensure you are running as expected (e.g., JVM, binaries, command to launch, etc.). The most common configurations are the JVM version and the launch command (assuming you are not relying on a provided container). Once again, how to configure it at 100% depends on your Cloud platform. Some providers need a marker file or read the property value in a particular file, while others just have it configured somewhere in their GUI. However, this point is a bit different from the previous one even if your configuration is not portable. It doesn’t affect your code or build; at worst you add a file in your project.

Heroku

Create your account

If you already have a Heroku account you can skip this part. If not, here are few screenshots showing you how fast and easy it is to register for a free Heroku account.
Once done, you can go on Heroku and download the Heroku client. There is a Maven plugin available, but we don’t need it for this post; plus it is easier to create an application with the provided client.

Once your client is installed, ensure your credentials are set up:

$ heroku login
Enter your Heroku credentials.
Email: xxx@yyyy.zzz
Password (typing will be hidden):
Authentication successful.

Grab a project!

To stay concrete, we’ll deploy the Tomitribe JAX-RS starter project on Heroku.

First checkout the project locally:

$ git clone https://github.com/tomitribe/tomee-jaxrs-starter-project.git myjaxrs

Note: I renamed the folder where the project was checked out because it is not recommended to use TomEE as a prefix for your own modules.

Link your Tomitribe JAX-RS project to Heroku

Now you have a project executed from your project folder (myjaxrs/), the command to create a Heroku application is:

myjaxrs $ heroku create
Creating glacial-lowlands-8637... done, stack is cedar-14
https://glacial-lowlands-8637.herokuapp.com/ | https://git.heroku.com/glacial-lowlands-8637.git
Git remote heroku added

This adds Heroku to your Git remote repository:

myjaxrs $ git remote
heroku
origin

Customize the JAX-RS starter project to run on Heroku

Heroku supports several types of packaging, but I’ll choose a simple one to use. Here, “simple” means easy to debug, easy to test, and easy to deploy and manage.

Today the challenge for an application server is to let you package them like a standard Java application, such as “main(String[])”. This is the trend as you can see with Spring Boot, Payara Micro, Wildfly Swarm and others, but it has been a core feature of TomEE for several years now.

As a quick reminder with TomEE, you can:

  • Create a shade to get an executable JAR or WAR file: `java -jar my-awesome-app.jar`
  • Run a WAR file from the command line: `java -jar tomee-embedded-uber.jar –path my-awesome-app.war`
  • Create an executable WAR file but executed in a real TomEE instance (i.e., a fork to ensure you don’t get classloading surprises as opposed to an embedded instance): `java -jar my-awesome-app-runner.war`
  • Reuse Tomcat Maven Plugin to create an executable war “Ã la Tomcat”

We decided to use the main method to run our application as any Java program.

For that purpose, you can modify the POM a bit. Here are the updates:

  • Java source/target version to 1.8 for the compiler
  • Java EE API to 7.0-SNAPSHOT
  • TomEE embedded version 7.0.0-SNAPSHOT as a compile dependency
  • Add Apache snapshot repository

Here is the final POM:

<?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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <!-- Change: groupId, artifactId, version -->
  <groupId>org.superbiz</groupId>
  <artifactId>tomee-rest-arquillian</artifactId>
  <version>1.0-SNAPSHOT</version>

  <packaging>war</packaging>

  <dependencies>
    <dependency>
      <groupId>org.apache.tomee</groupId>
      <artifactId>javaee-api</artifactId>
      <version>${openejb.javaee.api}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.tomee</groupId>
      <artifactId>tomee-embedded</artifactId>
      <version>${tomee.version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.tomee</groupId>
      <artifactId>arquillian-tomee-embedded</artifactId>
      <version>${tomee.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomee.maven</groupId>
        <artifactId>tomee-maven-plugin</artifactId>
        <version>${tomee.version}</version>
        <configuration>
          <tomeeVersion>${tomee.version}</tomeeVersion>
          <tomeeClassifier>jaxrs</tomeeClassifier>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository>
      <id>apache-snapshots</id>
      <url>https://repository.apache.org/content/repositories/snapshots/</url>
    </repository>
  </repositories>

  <properties>
    <tomee.version>7.0.0-SNAPSHOT</tomee.version>
    <openejb.javaee.api>7.0-SNAPSHOT</openejb.javaee.api>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <failOnMissingWebXml>false</failOnMissingWebXml>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
  </properties>
</project>

Note: Since TomEE 7.x targets Java EE 7 you do not need TomEE JAX-RS anymore because it is part of the default Web Profile in Java EE 7.

Now that we have our dependencies, we can write main method. As for Heroku, there’s nothing special except that the web port is passed through the environment in `PORT` variable.

You can either write your own main method:

import org.apache.tomee.embedded.Configuration;
import org.apache.tomee.embedded.Container;

import java.util.concurrent.CountDownLatch;

import static java.lang.Integer.parseInt;

public class HerokuRunner {
    public static void main(final String[] args) throws InterruptedException {
        try (Container container = new Container(
                    new Configuration().http(parseInt(System.getenv("PORT"))))
                .deployClasspathAsWebApp("/", null)) {
            new CountDownLatch(1).await();
        }
    }
}

Or just reuse the existing one from TomEE: org.apache.tomee.embedded.Main

We’ll go for this last solution and just wire $PORT environment variables to the port option of the main method. It means that we’ll need to launch this command to run our application:

# --as-war means deployClasspathAsWebApp() since we deploy a classpath "as" a war
# but this main also support executable wars
$ java -cp "the classpath" org.apache.tomee.embedded.Main --port=$PORT --as-war

If you run it manually (you can hardcode the port if it is just for a local test) you’ll get these logs:

INFOS - Starting TomEE from: /home/rmannibucau/Bureau/blogging/./apache-tomee
INFOS - Initializing ProtocolHandler ["http-bio-8080"]
INFOS - Starting service Tomcat
INFOS - Starting Servlet Engine: Apache Tomcat/8.0.21
INFOS - Starting ProtocolHandler ["http-bio-8080"]
INFOS - Using 'openejb.jdbc.datasource-creator=org.apache.tomee.jdbc.TomEEDataSourceCreator'
INFOS - ********************************************************************************
INFOS - OpenEJB http://tomee.apache.org/
INFOS - Startup: Tue May 05 13:43:19 CEST 2015
INFOS - Copyright 1999-2015 (C) Apache OpenEJB Project, All Rights Reserved.
INFOS - Version: 5.0.0-SNAPSHOT
INFOS - Build date: 20150505
INFOS - Build time: 09:41
INFOS - ********************************************************************************
INFOS - openejb.home = /home/rmannibucau/Bureau/blogging/apache-tomee
INFOS - openejb.base = /home/rmannibucau/Bureau/blogging/apache-tomee
INFOS - Created new singletonService org.apache.openejb.cdi.ThreadSingletonServiceImpl@1130520d
INFOS - Succeeded in installing singleton service
INFOS - openejb configuration file is '/home/rmannibucau/Bureau/blogging/apache-tomee/conf/openejb.xml'
INFOS - Configuring Service(id=Tomcat Security Service, type=SecurityService, provider-id=Tomcat Security Service)
INFOS - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager)
INFOS - Using 'openejb.system.apps=false'
INFOS - Using 'openejb.deployments.classpath=false'
INFOS - Creating TransactionManager(id=Default Transaction Manager)
INFOS - Creating SecurityService(id=Tomcat Security Service)
INFOS - Using 'openejb.servicemanager.enabled=false'
INFOS - Using 'openejb.deployments.classpath.filter.systemapps=false'
INFOS - Configuring enterprise application:
INFOS - Auto-deploying ejb ColorService: EjbDeployment(deployment-id=ColorService)
INFOS - Configuring Service(id=Default Managed Container, type=Container, provider-id=Default Managed Container)
INFOS - Auto-creating a container for bean .Comp1449987177: Container(type=MANAGED, id=Default Managed Container)
INFOS - Creating Container(id=Default Managed Container)
INFOS - Using directory /tmp for stateful session passivation
INFOS - Configuring Service(id=comp/DefaultManagedExecutorService, type=Resource, provider-id=Default Executor Service)
INFOS - Auto-creating a Resource with id 'comp/DefaultManagedExecutorService' of type 'javax.enterprise.concurrent.ManagedExecutorService for '.Comp1449987177'.
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedExecutorService' in bean .Comp1449987177 to Resource(id=comp/DefaultManagedExecutorService)
INFOS - Configuring Service(id=comp/DefaultManagedScheduledExecutorService, type=Resource, provider-id=Default Scheduled Executor Service)
INFOS - Auto-creating a Resource with id 'comp/DefaultManagedScheduledExecutorService' of type 'javax.enterprise.concurrent.ManagedScheduledExecutorService for '.Comp1449987177'.
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedScheduledExecutorService' in bean .Comp1449987177 to Resource(id=comp/DefaultManagedScheduledExecutorService)
INFOS - Configuring Service(id=comp/DefaultManagedThreadFactory, type=Resource, provider-id=Default Managed Thread Factory)
INFOS - Auto-creating a Resource with id 'comp/DefaultManagedThreadFactory' of type 'javax.enterprise.concurrent.ManagedThreadFactory for '.Comp1449987177'.
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedThreadFactory' in bean .Comp1449987177 to Resource(id=comp/DefaultManagedThreadFactory)
INFOS - Configuring Service(id=Default Singleton Container, type=Container, provider-id=Default Singleton Container)
INFOS - Auto-creating a container for bean ColorService: Container(type=SINGLETON, id=Default Singleton Container)
INFOS - Creating Container(id=Default Singleton Container)
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedExecutorService' in bean ColorService to Resource(id=comp/DefaultManagedExecutorService)
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedScheduledExecutorService' in bean ColorService to Resource(id=comp/DefaultManagedScheduledExecutorService)
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedThreadFactory' in bean ColorService to Resource(id=comp/DefaultManagedThreadFactory)
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedExecutorService' in bean EjbModule567656864.Comp734971558 to Resource(id=comp/DefaultManagedExecutorService)
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedScheduledExecutorService' in bean EjbModule567656864.Comp734971558 to Resource(id=comp/DefaultManagedScheduledExecutorService)
INFOS - Auto-linking resource-env-ref 'java:comp/DefaultManagedThreadFactory' in bean EjbModule567656864.Comp734971558 to Resource(id=comp/DefaultManagedThreadFactory)
INFOS - Enterprise application "" loaded.
INFOS - Assembling app:
INFOS - Jndi(name=ColorServiceLocalBean) --> Ejb(deployment-id=ColorService)
INFOS - Jndi(name=global/ColorService!org.superbiz.ColorService) --> Ejb(deployment-id=ColorService)
INFOS - Jndi(name=global/ColorService) --> Ejb(deployment-id=ColorService)
INFOS - Existing thread singleton service in SystemInstance(): org.apache.openejb.cdi.ThreadSingletonServiceImpl@1130520d
INFOS - OpenWebBeans Container is starting...
INFOS - Adding OpenWebBeansPlugin : [CdiPlugin]
INFOS - Adding OpenWebBeansPlugin : [OpenWebBeansJsfPlugin]
INFOS - No beans.xml in file:/tmp/heroku/myjaxrs/target/classes/ looking all classes to find CDI beans, maybe think to add a beans.xml or add it to exclusions.list
INFOS - All injection points were validated successfully.
INFOS - OpenWebBeans Container has started, it took 523 ms.
INFOS - Created Ejb(deployment-id=ColorService, ejb-name=ColorService, container=Default Singleton Container)
INFOS - Started Ejb(deployment-id=ColorService, ejb-name=ColorService, container=Default Singleton Container)
INFOS - using default host: localhost
INFOS - ------------------------- localhost -> /
INFOS - Using 'openejb.session.manager=org.apache.tomee.catalina.session.QuickSessionManager'
INFOS - The start() method was called on component [org.apache.catalina.webresources.DirResourceSet@724b939e] after start() had already been called. The second call will be ignored.
INFOS - The start() method was called on component [org.apache.catalina.webresources.JarResourceSet@6f8aba08] after start() had already been called. The second call will be ignored.
INFOS - At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
INFOS - Using providers:
INFOS -      org.apache.johnzon.jaxrs.JohnzonProvider@45900b64
INFOS -      org.apache.cxf.jaxrs.provider.JAXBElementProvider@f2a1813
INFOS -      org.apache.johnzon.jaxrs.JsrProvider@79f90a3a
INFOS -      org.apache.johnzon.jaxrs.WadlDocumentMessageBodyWriter@22bdb1d0
INFOS -      org.apache.openejb.server.cxf.rs.EJBAccessExceptionMapper@67b355c8
INFOS -      org.apache.cxf.jaxrs.validation.ValidationExceptionMapper@388623ad
INFOS - REST Application: http://localhost:8080/              -> org.apache.openejb.server.rest.InternalApplication
INFOS -      Service URI: http://localhost:8080/color         ->  EJB org.superbiz.ColorService
INFOS -               GET http://localhost:8080/color/        ->      String getColor()
INFOS -               GET http://localhost:8080/color/object  ->      Color getColorObject()
INFOS -              POST http://localhost:8080/color/{color} ->      void setColor(String)
INFOS - Deployed Application(path=)

And if you hit the GET URL logged at the end (http://localhost:8080/color/object) you’ll get this response:

{"b":0,"r":231,"g":113,"name":"orange"}

Now, we know how to run our application in embedded mode. Let’s configure it for Heroku.

Let Heroku know how to handle your application

First, and to avoid surprises, we’ll force the JVM to version 1.8 on the Heroku platform for our application. This step at the moment is optional since it is the default case, but it will help avoid surprises should the version change.

To do so, just add a system.properties file in the root of your project containing:

java.runtime.version=1.8

To launch our application, we need to specify the command to run. To do it, just create a Procfile file in the root of the project with:

web:    java -cp target/classes:target/dependency/* org.apache.tomee.embedded.Main --port=$PORT --as-war

Ensure your classpath is ready!

To build our classpath, we’ll simply rely on maven-dependency-plugin:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>2.9</version>
  <executions>
    <execution>
      <id>copy-dependencies</id>
      <phase>package</phase>
      <goals><goal>copy-dependencies</goal></goals>
    </execution>
  </executions>
</plugin>

And here we are.

Time to deploy!

To deploy, simply commit the new files along with changes, and push your commit to Heroku:

myjaxrs $ git commit -a -m "updating the application to be heroku compliant" && git push heroku master

This will trigger a build on Heroku. The first build can be long since it downloads all dependencies, but it is fast once completed and will run Procfile of the application. (Download sections has been removed to keep the logs readable):

myjaxrs $ git push heroku master
Counting objects: 94, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (69/69), done.
Writing objects: 100% (94/94), 23.34 KiB | 0 bytes/s, done.
Total 94 (delta 26), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Java app detected
remote: -----> Installing OpenJDK 1.8... done
remote: -----> Installing Maven 3.3.1... done
remote: -----> Executing: mvn -B -DskipTests=true clean install
remote:        [INFO] Scanning for projects...
remote:        [INFO]                                                                         
remote:        [INFO] ------------------------------------------------------------------------
remote:        [INFO] Building tomee-rest-arquillian 1.0-SNAPSHOT
remote:        [INFO] ------------------------------------------------------------------------
remote:        [INFO] 
remote:        [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ tomee-rest-arquillian ---
remote:        [INFO] 
remote:        [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ tomee-rest-arquillian ---
remote:        [INFO] Using 'UTF-8' encoding to copy filtered resources.
remote:        [INFO] skip non existing resourceDirectory /tmp/build_6cfd2d281c66d12debd9abd55200c37e/src/main/resources
remote:        [INFO] 
remote:        [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ tomee-rest-arquillian ---
remote:        [INFO] Changes detected - recompiling the module!
remote:        [INFO] Compiling 2 source files to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/classes
remote:        [INFO] 
remote:        [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ tomee-rest-arquillian ---
remote:        [INFO] Using 'UTF-8' encoding to copy filtered resources.
remote:        [INFO] Copying 1 resource
remote:        [INFO] 
remote:        [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ tomee-rest-arquillian ---
remote:        [INFO] Changes detected - recompiling the module!
remote:        [INFO] Compiling 1 source file to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/test-classes
remote:        [INFO] 
remote:        [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ tomee-rest-arquillian ---
remote:        [INFO] Tests are skipped.
remote:        [INFO] 
remote:        [INFO] --- maven-war-plugin:2.2:war (default-war) @ tomee-rest-arquillian ---
remote:        [INFO] Packaging webapp
remote:        [INFO] Assembling webapp [tomee-rest-arquillian] in [/tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/tomee-rest-arquillian-1.0-SNAPSHOT]
remote:        [INFO] Processing war project
remote:        [INFO] Webapp assembled in [640 msecs]
remote:        [INFO] Building war: /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/tomee-rest-arquillian-1.0-SNAPSHOT.war
remote:        [INFO] 
remote:        [INFO] --- maven-dependency-plugin:2.9:copy-dependencies (copy-dependencies) @ tomee-rest-arquillian ---
remote:        [INFO] Copying commons-lang3-3.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-lang3-3.4.jar
remote:        [INFO] Copying sxc-runtime-0.8.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/sxc-runtime-0.8.jar
remote:        [INFO] Copying shrinkwrap-api-1.2.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/shrinkwrap-api-1.2.2.jar
remote:        [INFO] Copying arquillian-config-api-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-config-api-1.1.8.Final.jar
remote:        [INFO] Copying openejb-javaagent-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-javaagent-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying cxf-rt-rs-service-description-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-rs-service-description-3.0.4.jar
remote:        [INFO] Copying tomee-common-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-common-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying cxf-rt-rs-security-oauth2-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-rs-security-oauth2-3.0.4.jar
remote:        [INFO] Copying tomee-loader-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-loader-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying openejb-api-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-api-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying serp-1.15.1.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/serp-1.15.1.jar
remote:        [INFO] Copying myfaces-api-2.2.8.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/myfaces-api-2.2.8.jar
remote:        [INFO] Copying bval-core-1.1.0-alpha-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/bval-core-1.1.0-alpha-SNAPSHOT.jar
remote:        [INFO] Copying openwebbeans-impl-1.5.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openwebbeans-impl-1.5.0.jar
remote:        [INFO] Copying tomcat-jasper-el-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-jasper-el-8.0.21.jar
remote:        [INFO] Copying tomee-myfaces-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-myfaces-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying tomcat-util-scan-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-util-scan-8.0.21.jar
remote:        [INFO] Copying commons-logging-1.1.1.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-logging-1.1.1.jar
remote:        [INFO] Copying jaxb-api-2.2.6.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/jaxb-api-2.2.6.jar
remote:        [INFO] Copying commons-dbcp-1.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-dbcp-1.4.jar
remote:        [INFO] Copying hawtbuf-1.11.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/hawtbuf-1.11.jar
remote:        [INFO] Copying arquillian-transaction-impl-base-1.0.1.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-transaction-impl-base-1.0.1.Final.jar
remote:        [INFO] Copying sxc-jaxb-core-0.8.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/sxc-jaxb-core-0.8.jar
remote:        [INFO] Copying shrinkwrap-spi-1.2.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/shrinkwrap-spi-1.2.2.jar
remote:        [INFO] Copying cxf-rt-rs-security-jose-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-rs-security-jose-3.0.4.jar
remote:        [INFO] Copying geronimo-connector-3.1.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/geronimo-connector-3.1.2.jar
remote:        [INFO] Copying tomcat-tribes-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-tribes-8.0.21.jar
remote:        [INFO] Copying arquillian-container-impl-base-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-container-impl-base-1.1.8.Final.jar
remote:        [INFO] Copying shrinkwrap-impl-base-1.2.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/shrinkwrap-impl-base-1.2.2.jar
remote:        [INFO] Copying shrinkwrap-descriptors-impl-base-2.0.0-alpha-7.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/shrinkwrap-descriptors-impl-base-2.0.0-alpha-7.jar
remote:        [INFO] Copying tomcat-websocket-api-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-websocket-api-8.0.21.jar
remote:        [INFO] Copying xbean-naming-4.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/xbean-naming-4.2.jar
remote:        [INFO] Copying johnzon-mapper-0.7-incubating.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/johnzon-mapper-0.7-incubating.jar
remote:        [INFO] Copying tomee-util-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-util-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying arquillian-transaction-api-1.0.1.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-transaction-api-1.0.1.Final.jar
remote:        [INFO] Copying xbean-bundleutils-4.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/xbean-bundleutils-4.2.jar
remote:        [INFO] Copying commons-collections-3.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-collections-3.2.jar
remote:        [INFO] Copying myfaces-impl-2.2.8.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/myfaces-impl-2.2.8.jar
remote:        [INFO] Copying openejb-core-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-core-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying activemq-kahadb-store-5.11.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/activemq-kahadb-store-5.11.0.jar
remote:        [INFO] Copying commons-codec-1.3.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-codec-1.3.jar
remote:        [INFO] Copying cxf-rt-rs-security-cors-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-rs-security-cors-3.0.4.jar
remote:        [INFO] Copying quartz-openejb-shade-2.2.1.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/quartz-openejb-shade-2.2.1.jar
remote:        [INFO] Copying cxf-core-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-core-3.0.4.jar
remote:        [INFO] Copying openejb-ejbd-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-ejbd-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying tomcat-catalina-ha-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-catalina-ha-8.0.21.jar
remote:        [INFO] Copying arquillian-core-spi-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-core-spi-1.1.8.Final.jar
remote:        [INFO] Copying arquillian-core-impl-base-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-core-impl-base-1.1.8.Final.jar
remote:        [INFO] Copying javaee-api-7.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/javaee-api-7.0-SNAPSHOT.jar
remote:        [INFO] Copying openjpa-2.4.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openjpa-2.4.0.jar
remote:        [INFO] Copying johnzon-jaxrs-0.7-incubating.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/johnzon-jaxrs-0.7-incubating.jar
remote:        [INFO] Copying openejb-http-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-http-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying openejb-rest-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-rest-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying cxf-rt-rs-client-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-rs-client-3.0.4.jar
remote:        [INFO] Copying commons-beanutils-1.8.3.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-beanutils-1.8.3.jar
remote:        [INFO] Copying xbean-finder-shaded-4.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/xbean-finder-shaded-4.2.jar
remote:        [INFO] Copying activemq-client-5.11.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/activemq-client-5.11.0.jar
remote:        [INFO] Copying bval-jsr-1.1.0-alpha-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/bval-jsr-1.1.0-alpha-SNAPSHOT.jar
remote:        [INFO] Copying openejb-jpa-integration-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-jpa-integration-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying tomcat-servlet-api-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-servlet-api-8.0.21.jar
remote:        [INFO] Copying commons-pool-1.5.7.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-pool-1.5.7.jar
remote:        [INFO] Copying openwebbeans-web-1.5.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openwebbeans-web-1.5.0.jar
remote:        [INFO] Copying activemq-protobuf-1.1.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/activemq-protobuf-1.1.jar
remote:        [INFO] Copying tomcat-catalina-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-catalina-8.0.21.jar
remote:        [INFO] Copying openwebbeans-ejb-1.5.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openwebbeans-ejb-1.5.0.jar
remote:        [INFO] Copying arquillian-transaction-spi-1.0.1.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-transaction-spi-1.0.1.Final.jar
remote:        [INFO] Copying oro-2.0.8.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/oro-2.0.8.jar
remote:        [INFO] Copying arquillian-common-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-common-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying activemq-ra-5.11.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/activemq-ra-5.11.0.jar
remote:        [INFO] Copying slf4j-api-1.7.7.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/slf4j-api-1.7.7.jar
remote:        [INFO] Copying openejb-jstl-1.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-jstl-1.2.jar
remote:        [INFO] Copying shrinkwrap-descriptors-impl-javaee-2.0.0-alpha-7.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/shrinkwrap-descriptors-impl-javaee-2.0.0-alpha-7.jar
remote:        [INFO] Copying arquillian-tomee-embedded-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-tomee-embedded-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying activemq-broker-5.11.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/activemq-broker-5.11.0.jar
remote:        [INFO] Copying tomcat-websocket-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-websocket-8.0.21.jar
remote:        [INFO] Copying tomcat-juli-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-juli-8.0.21.jar
remote:        [INFO] Copying arquillian-test-spi-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-test-spi-1.1.8.Final.jar
remote:        [INFO] Copying openejb-jee-accessors-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-jee-accessors-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying openejb-loader-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-loader-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying arquillian-container-spi-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-container-spi-1.1.8.Final.jar
remote:        [INFO] Copying arquillian-core-api-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-core-api-1.1.8.Final.jar
remote:        [INFO] Copying arquillian-container-test-spi-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-container-test-spi-1.1.8.Final.jar
remote:        [INFO] Copying commons-lang-2.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-lang-2.4.jar
remote:        [INFO] Copying tomee-juli-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-juli-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying arquillian-junit-container-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-junit-container-1.1.8.Final.jar
remote:        [INFO] Copying tomcat-jdbc-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-jdbc-8.0.21.jar
remote:        [INFO] Copying tomcat-jasper-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-jasper-8.0.21.jar
remote:        [INFO] Copying openejb-cxf-transport-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-cxf-transport-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying tomcat-jni-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-jni-8.0.21.jar
remote:        [INFO] Copying tomee-jaxrs-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-jaxrs-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying arquillian-junit-core-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-junit-core-1.1.8.Final.jar
remote:        [INFO] Copying shrinkwrap-descriptors-spi-2.0.0-alpha-7.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/shrinkwrap-descriptors-spi-2.0.0-alpha-7.jar
remote:        [INFO] Copying tomcat-api-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-api-8.0.21.jar
remote:        [INFO] Copying tomcat-coyote-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-coyote-8.0.21.jar
remote:        [INFO] Copying cxf-rt-rs-extension-providers-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-rs-extension-providers-3.0.4.jar
remote:        [INFO] Copying hamcrest-core-1.3.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/hamcrest-core-1.3.jar
remote:        [INFO] Copying woodstox-core-asl-4.4.1.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/woodstox-core-asl-4.4.1.jar
remote:        [INFO] Copying tomee-jdbc-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-jdbc-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying howl-1.0.1-1.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/howl-1.0.1-1.jar
remote:        [INFO] Copying activemq-jdbc-store-5.11.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/activemq-jdbc-store-5.11.0.jar
remote:        [INFO] Copying swizzle-stream-1.6.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/swizzle-stream-1.6.2.jar
remote:        [INFO] Copying openwebbeans-ee-common-1.5.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openwebbeans-ee-common-1.5.0.jar
remote:        [INFO] Copying openwebbeans-el22-1.5.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openwebbeans-el22-1.5.0.jar
remote:        [INFO] Copying tomcat-dbcp-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-dbcp-8.0.21.jar
remote:        [INFO] Copying arquillian-container-test-api-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-container-test-api-1.1.8.Final.jar
remote:        [INFO] Copying mbean-annotation-api-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/mbean-annotation-api-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying arquillian-test-api-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-test-api-1.1.8.Final.jar
remote:        [INFO] Copying cxf-rt-management-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-management-3.0.4.jar
remote:        [INFO] Copying openwebbeans-jsf-1.5.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openwebbeans-jsf-1.5.0.jar
remote:        [INFO] Copying tomcat-el-api-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-el-api-8.0.21.jar
remote:        [INFO] Copying javaee-api-7.0-SNAPSHOT-tomcat.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/javaee-api-7.0-SNAPSHOT-tomcat.jar
remote:        [INFO] Copying openejb-jee-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-jee-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying shrinkwrap-descriptors-api-base-2.0.0-alpha-7.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/shrinkwrap-descriptors-api-base-2.0.0-alpha-7.jar
remote:        [INFO] Copying commons-digester-1.8.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-digester-1.8.jar
remote:        [INFO] Copying tomcat-jsp-api-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-jsp-api-8.0.21.jar
remote:        [INFO] Copying openwebbeans-ee-1.5.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openwebbeans-ee-1.5.0.jar
remote:        [INFO] Copying cxf-rt-frontend-jaxrs-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-frontend-jaxrs-3.0.4.jar
remote:        [INFO] Copying openejb-server-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-server-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying velocity-1.6.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/velocity-1.6.4.jar
remote:        [INFO] Copying activemq-openwire-legacy-5.11.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/activemq-openwire-legacy-5.11.0.jar
remote:        [INFO] Copying junit-4.11.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/junit-4.11.jar
remote:        [INFO] Copying xbean-asm5-shaded-4.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/xbean-asm5-shaded-4.2.jar
remote:        [INFO] Copying johnzon-core-0.7-incubating.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/johnzon-core-0.7-incubating.jar
remote:        [INFO] Copying commons-beanutils-core-1.8.3.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-beanutils-core-1.8.3.jar
remote:        [INFO] Copying geronimo-transaction-3.1.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/geronimo-transaction-3.1.2.jar
remote:        [INFO] Copying commons-cli-1.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/commons-cli-1.2.jar
remote:        [INFO] Copying arquillian-test-impl-base-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-test-impl-base-1.1.8.Final.jar
remote:        [INFO] Copying xmlschema-core-2.2.1.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/xmlschema-core-2.2.1.jar
remote:        [INFO] Copying tomcat-util-8.0.21.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomcat-util-8.0.21.jar
remote:        [INFO] Copying openejb-client-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-client-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying xbean-reflect-4.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/xbean-reflect-4.2.jar
remote:        [INFO] Copying ecj-4.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/ecj-4.4.jar
remote:        [INFO] Copying arquillian-config-impl-base-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-config-impl-base-1.1.8.Final.jar
remote:        [INFO] Copying stax2-api-3.1.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/stax2-api-3.1.4.jar
remote:        [INFO] Copying openwebbeans-spi-1.5.0.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openwebbeans-spi-1.5.0.jar
remote:        [INFO] Copying jaxb-impl-2.2.6.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/jaxb-impl-2.2.6.jar
remote:        [INFO] Copying arquillian-tomee-common-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-tomee-common-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying slf4j-jdk14-1.7.7.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/slf4j-jdk14-1.7.7.jar
remote:        [INFO] Copying openejb-cxf-rs-5.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/openejb-cxf-rs-5.0.0-SNAPSHOT.jar
remote:        [INFO] Copying geronimo-javamail_1.4_mail-1.8.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/geronimo-javamail_1.4_mail-1.8.4.jar
remote:        [INFO] Copying hsqldb-2.3.2.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/hsqldb-2.3.2.jar
remote:        [INFO] Copying cxf-rt-rs-extension-search-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-rs-extension-search-3.0.4.jar
remote:        [INFO] Copying arquillian-container-test-impl-base-1.1.8.Final.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-container-test-impl-base-1.1.8.Final.jar
remote:        [INFO] Copying shrinkwrap-descriptors-api-javaee-2.0.0-alpha-7.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/shrinkwrap-descriptors-api-javaee-2.0.0-alpha-7.jar
remote:        [INFO] Copying tomee-embedded-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-embedded-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying cxf-rt-transports-http-3.0.4.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/cxf-rt-transports-http-3.0.4.jar
remote:        [INFO] Copying tomee-catalina-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/tomee-catalina-2.0.0-SNAPSHOT.jar
remote:        [INFO] Copying arquillian-openejb-transaction-provider-2.0.0-SNAPSHOT.jar to /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/dependency/arquillian-openejb-transaction-provider-2.0.0-SNAPSHOT.jar
remote:        [INFO] 
remote:        [INFO] --- maven-install-plugin:2.4:install (default-install) @ tomee-rest-arquillian ---
remote:        [INFO] Installing /tmp/build_6cfd2d281c66d12debd9abd55200c37e/target/tomee-rest-arquillian-1.0-SNAPSHOT.war to /app/tmp/cache/.m2/repository/org/superbiz/tomee-rest-arquillian/1.0-SNAPSHOT/tomee-rest-arquillian-1.0-SNAPSHOT.war
remote:        [INFO] Installing /tmp/build_6cfd2d281c66d12debd9abd55200c37e/pom.xml to /app/tmp/cache/.m2/repository/org/superbiz/tomee-rest-arquillian/1.0-SNAPSHOT/tomee-rest-arquillian-1.0-SNAPSHOT.pom
remote:        [INFO] ------------------------------------------------------------------------
remote:        [INFO] BUILD SUCCESS
remote:        [INFO] ------------------------------------------------------------------------
remote:        [INFO] Total time: 02:56 min
remote:        [INFO] Finished at: 2015-05-05T11:57:17+00:00
remote:        [INFO] Final Memory: 36M/324M
remote:        [INFO] ------------------------------------------------------------------------
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote: 
remote: -----> Compressing... done, 161.4MB
remote: -----> Launching... done, v6
remote:        https://glacial-lowlands-8637.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/glacial-lowlands-8637.git
 * [new branch]      master -> master

Note: You don’t need to build a WAR file for Heroku. You can configure the maven-war-plugin to be skipped if desired.

As you can see, the application is now available at https://glacial-lowlands-8637.herokuapp.com/. This means we can now hit https://glacial-lowlands-8637.herokuapp.com/color/object to get the same response locally.

Access logs are accessible using Heroku client. You’ll get the same startup locally and a line for each request (in yellow):

myjaxrs $ heroku logs
# ....
2015-05-05T12:00:36.157947+00:00 heroku[router]: at=info method=GET path="/color/object" host=glacial-lowlands-8637.herokuapp.com request_id=xxxxxx-9424-47c5-8056-yyyyyyy fwd="1.2.3.4" dyno=web.1 connect=1ms service=5ms status=200 bytes=212

Going further: get a Database

By default, Heroku is able to provide you a PostgreSQL database. Its configuration is injected through environment variable:

DATABASE_URL

Default value looks like:

postgres://user:password@host:port/database

All required information is here, but it looks more like a PHP URL than a JDBC one.

Heroku GUI allows you to edit this variable and add some others. Thus, it would be easy to use TomEE resource placeholders to wire this configuration to a TomEE datasource. Suppose you define JDBC_URL, JDBC_User, JDBC_PASSWORD then you could define the resource through system properties on your command line (Procfile) like this:

web:    java -DmyDb=new://Resource?type=DataSource -DmyDb.JdbcUrl=$JDBC_URL -DmyDb.UserName=$JDBC_USER -DmyDb.Password=$JDBC_PASSWORD -cp target/classes:target/dependency/* org.apache.tomee.embedded.Main --port=$PORT --as-war

However changing default environment variables is not that nice because it makes your application “custom” and it is harder to share the knowledge about its monitoring and configuration setup.

To avoid that, the alternative would be to make TomEE understand this URL. Since we are embedded we could hack it just before running TomEE and set it as system properties or container properties directly in our main method. This would work, but I’d like to share another solution.

TomEE supports properties-provider attribute on resources since last year. This property takes a fully qualified name of a class providing properties (configuration) for the resource on which it is defined. In our case it is easy to implement a HerokuPropertiesProvider to read the DATABASE_URL and convert it into properties JdbcUrl, UserNamePassword and JdbcDriver.

I will not detail the implementation here (parsing the DATABASE_URL and recreating a JDBC URL from it) since it is now built-in in TomEE: org.apache.openejb.resource.heroku.HerokuDatabasePropertiesProvider.

What does it mean for us? To get our Heroku database wired to myDb in the application, we simply need to define:

-DmyDb=new://Resource?type=DataSource&properties-provider=org.apache.openejb.resource.heroku.HerokuDatabasePropertiesProvider

Important: don’t forget to quote the system property to avoid issues with the &in Procfile since it is a shell command:

web:    java "-DmyDb=new://Resource?type=DataSource&properties-provider=org.apache.openejb.resource.heroku.HerokuDatabasePropertiesProvider" -cp target/classes:target/dependency/* org.apache.tomee.embedded.Main --port=$PORT --as-war

Finally, as the default database provided by Heroku is a PostgreSQL, we need to add the driver in our POM:

<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <version>9.3-1102-jdbc4</version>
</dependency>

You can now add a bean to check if your database configuration is done well. For instance, we can use a @Singleton EJB with a @Startup method:

import java.sql.Connection;
import java.sql.SQLException;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.sql.DataSource;

@Singleton
@Startup
public class DbLogger {
    @Resource(name = "myDb")
    private DataSource ds;

    @PostConstruct
    private void log() {
        try (Connection connection = ds.getConnection()) {
            System.out.println(">>> " + connection.getMetaData().getURL());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

When redeploying the application with this class (i.e., Git commit and push on Heroku master branch), you will see the following in the logs:

2015-05-05T12:40:22.044350+00:00 app[web.1]: >>> jdbc:postgresql://host:port/database

This indicates everything is ready to use myDb as JTA datasource in a persistence.xml. ;)

Conclusion

There are many alternatives to deploy on a Cloud platform that allow you to provide the launch and stop command for your application. It’s nice to see that TomEE is able to integrate smoothly with whatever solution you choose for your deployment.

Some things that you need to take into account when using it for real applications, especially Heroku:

  • Using Git and several remotes is nice for a developer, but do you want to share the same repository between production teams and developers?
  • It’s quite easy to push binaries (e.g., a shade myapp.jar) and replace the command in Procfile to execute it, but do you want to build on your production nodes?
  • It’s nice to have the ability to easily rollback whatever solution you choose thanks to Git. Plus, the learning curve is quite small because it’s a well-known technology. Do you need more reasons?

Finally the TomEE JAX-RS starter project now has a heroku branch ready to deploy on Heroku if you don’t want to do all these configurations yourself!

Have fun forking us!


Romain

About the author

Romain Manni-Bucau

Senior Software Engineer
http://rmannibucau.wordpress.com
Follow Romain Manni-Bucau
Romain is a contributor of the Apache TomEE project since July 2010 and a Senior Software Engineer at Tomitribe. In his plethora of Apache projects, he’s involved in OpenEJB, OpenWebBeans, Geronimo, CFX, BVal and DeltaSpike. Romain is a founding member of the Sirona and BatchEE project and brought JCache implementation to the Apache Commons JCS project. He regularly speaks at JUG and conferences to spread the word about all the Apache goodness. Since Java EE 6, he is convinced that REST architectures and design are the future of Java EE and not only for web technologies. Romain started as a Java EE expert at Atos transversal unit. He later joined Swissquote Bank as a backend developer. Today he is a part of the Tomitribe adventure and continues his mission with all the Apache projects, especially TomEE. Romain likes "coding on the edge," always proposing solutions of tomorrow and that's why he joined the Tribe!