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: [email protected] 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
, UserName
, Password
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!