Run your Java Tomcat Application for Free on OpenShift's PaaS


Our users have asked for official Tomcat support on OpenShift and the team has listened! I am happy to annouce that OpenShift now officially supports Tomcat via our JBoss EWS cartridge. Adding this support allows developers to easily deploy their Spring applications to OpenShift while taking advantage of all the great features that a platform as a service (PaaS) provides.

Note: With a few tweaks to your application datasources, you can also deploy Spring based applications to the supported AS7 or EAP JBoss servers provided by OpenShift.

In this blog, I will talk about how you can port a Spring framework reference application to OpenShift. The reference application we will be using in this blog is the famous travel application. The code for this travel application exists in the official Spring subversion repository. OpenShift uses git by default, so I have pushed the code to a new git repository that I created for this post. This application is based on Spring 3.0 using both Spring MVC and Spring Webflow for the frontend. The application also uses JPA to persist entities to HSQLDB.

Step 1: Sign up for an OpenShift Account

If you don’t already have an OpenShift account, head on over to the website and sign up. It is completely free and Red Hat gives every user three free Gears on which to run your applications. At the time of this writing, the combined resources allocated for each user is 1.5 GB of memory and 3 GB of disk space.

Step 2: Install the client tools on your machine

Note: If you would rather watch a screencast of this step, check out the following videos where I demo how to install the client tools.

Windows

Linux Ubuntu

Linux Fedora

OSX

The OpenShift client tools are written in a very popular programming language called Ruby. With OSX 10.6 or later and most Linux distributions, Ruby is installed by default so installing the client tools is a snap. Simply issue the following command on your terminal application:

sudo gem install rhc

Step 3 : Setting up OpenShift

The rhc client tool makes it very easy to setup your OpenShift instance with ssh keys, git and your applications namespace. The namespace is a unique name per user which becomes part of your application url. For example, if your namespace is cix and application name is travel then url of the application will be travel-cix.rhcloud.com. The command is shown below.

rhc setup

Step 4 : Creating a Tomcat Gear

After setting up your OpenShift account, we need to create a gear which will contain the Tomcat environment where we can deploy our application. The -t option specifies the type of the application you want to create. In this blog, we are using jbossews. JBoss EWS is the JBoss Enterprise Web Server built on top on Apache Tomcat. JBoss Enterprise Web Server is a fully-integrated and certified set of components for hosting Java web applications. It is comprised of the web server (Apache HTTP Server), the Apache Tomcat Servlet container, load balancers (mod_jk and mod_cluster), and the Tomcat Native library.To create the tomcat application, execute the command shown below:

rhc app create travel jbossews

This command will create a directory named travel which contains a OpenShift template project. The application will be accessible now. Let us take a look at what's inside the travel directory to better understand the layout of the EWS container.

  1. src directory : This directory contains the source code for the template application generated by OpenShift. You need to add your application source code here. The src folder helps in achieving source code deployment when following the standard maven directory conventions.
  2. pom.xml file: The Java applications created by OpenShift are Maven based projects. So, a pom.xml file is required when you do source code deployment on OpenShift. This pom.xml has a profile called "openshift" which will be executed when you push code to OpenShift as shown below. This profile will create a ROOT war file based upon your application source code.
    <profiles>
        <profile>
            <id>openshift</id>
            <build>
                <finalName>travel</finalName>
                <plugins>
                    <plugin>
                        <artifactId>maven-war-plugin</artifactId>
                        <version>2.1.1</version>
                        <configuration>
                            <outputDirectory>webapps</outputDirectory>
                            <warName>ROOT</warName>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
  3. webapps directory: You should use this directory in case you want to do binary deployments on OpenShift i.e. you want to deploy a war file directly instead of pushing the source code.
  4. .git directory : It is a local git repository
  5. .openshift directory : This is an OpenShift specific directory which can be used for following:
    1. the files under the action_hooks sub-directory allow you to hook into the application lifecycle.
    2. the files under the config sub-directory allow you to make changes to tomcat configuration. The directory contains tomcat specific configuration files like server.xml, context.xml, tomcat-users.xml, etc.
    3. the files under the cron sub-directory are used when you add the cron cartridge to your application. This allows you to run scripts or jobs on a periodic basis.
    4. the files under marker sub-directory allow you to specify whether you want to use Java 6 or Java 7, or you want to do hot deploy, or debug the application running in cloud, etc. The README file under the marker sub-directory provides all the marker files available.

Step 5 : Pulling Travel Application Source Code from GitHub

The next step is pulling the code from github. To do this run the following commands.

git rm -rf src/ pom.xml
git commit -am "removed default files"
git remote add upstream -m master git://github.com/shekhargulati/spring-travel.git
git pull -s recursive -X theirs upstream master

You can compile the code and run the application on your local system by executing mvn tomcat:run command. To deploy on OpenShift we have to add openshift maven profile which I talked earlier in the post. Add the profile to pom.xml as shown below. I have removed dependencies, repository, and plugins from pom.xml for brevity.

<?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>
    <groupId>org.springframework.samples</groupId>
    <artifactId>travel</artifactId>
    <name>travel</name>
    <packaging>war</packaging>
    <version>1.0.0-BUILD-SNAPSHOT</version>
    <properties>
        <java-version>1.6</java-version>
        <org.springframework-version>3.0.5.RELEASE</org.springframework-version>
        <org.springwebflow-version>2.2.1.RELEASE</org.springwebflow-version>
        <org.springsecurity-version>3.0.5.RELEASE</org.springsecurity-version>
        <org.aspectj-version>1.6.9</org.aspectj-version>
        <org.slf4j-version>1.6.1</org.slf4j-version>
    </properties>
    <dependencies>
        <!-- Dependencies -->
 
    </dependencies>
    <repositories>
        <!-- Repositories -->
 
    </repositories>
    <build>
        <plugins>
            <!--Plugins -->
        </plugins>
    </build>
 
    <profiles>
        <profile>
            <id>openshift</id>
            <build>
                <finalName>travel</finalName>
                <plugins>
                    <plugin>
                        <artifactId>maven-war-plugin</artifactId>
                        <version>2.1.1</version>
                        <configuration>
                            <outputDirectory>webapps</outputDirectory>
                            <warName>ROOT</warName>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

Step 6 : Deploying the application to OpenShift

Next you can push the code to git repository managed by OpenShift. To push the code execute the command shown below. This will run the maven openshift profile.

git commit -am "added openshift profile to pom.xml"
git push

Next when you go to your app, you will see the application homepage as shown below.

Step 7 : Adding PostgreSQL support

By default, the travel application comes bundled with an in-memory database called HSQLDB. Let's update the code to use a very popular open source RDMS called PostgreSQL. Let's first add PostgreSQL support to our application.

rhc cartridge add postgresql -a travel

The output returned by the command will be something like as shown below.

RESULT:
 
PostgreSQL 8.4 database added.  Please make note of these credentials:
 
   Root User: admin
   Root Password: EwiuS5_i4vfp
   Database Name: travel
 
Connection URL: postgresql://$OPENSHIFT_POSTGRESQL_DB_HOST:$OPENSHIFT_POSTGRESQL_DB_PORT/

The one change that you will find in the output shown above is that there are new environment variables for PostgreSQL databases.

Step 8 : Application Code changes required for PostgreSQL database

To make the application use PostgreSQL instead of HSQLDB we have to make few changes. Let's take a look at them one by one.

  1. Adding maven dependencies : The first change that we have to make is to add Maven dependencies for PostgreSQL database driver and commons-dbcp for connection pooling as shown below.
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.3</version>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
            <exclusion>
                <groupId>xml-apis</groupId>
                <artifactId>xml-apis</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>8.4-702.jdbc4</version>
    </dependency>
  2. Updating persistence.xml  : The second change that we have to make is to update the peristence.xml with PostgreSQL hibernate dialect instead of HSQLDB. The persistence.xml is shown below.
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
                 version="1.0">
       <persistence-unit name="travelDatabase">
          <provider>org.hibernate.ejb.HibernatePersistence</provider>
          <class>org.springframework.samples.travel.User</class>
          <class>org.springframework.samples.travel.Booking</class>
          <class>org.springframework.samples.travel.Hotel</class>
          <properties>
             <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
             <property name="hibernate.hbm2ddl.auto" value="create-drop" />
             <property name="hibernate.show_sql" value="true"/>
             <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/>
          </properties>
       </persistence-unit>
    </persistence>
  3. Updating data.xml application context file: The last change that we have to make is in data.xm application context file. Replace hsqldb datasource with BasicDataSource in data.xml application context file as shown below and add PropertyPlaceholderConfigurer bean to replace ${...} placeholders.
    <context:property-placeholder/>
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName" value="org.postgresql.Driver"></property>
            <property name="username" value="${OPENSHIFT_POSTGRESQL_DB_USERNAME}"></property>
            <property name="password" value="${OPENSHIFT_POSTGRESQL_DB_PASSWORD}"></property>
            <property name="url" value="jdbc:postgresql://${OPENSHIFT_POSTGRESQL_DB_HOST}:${OPENSHIFT_POSTGRESQL_DB_PORT}/${OPENSHIFT_APP_NAME}"></property>
            <property name="testOnBorrow" value="true"></property>
            <property name="testOnReturn" value="true"></property>
            <property name="testWhileIdle" value="true"></property>
            <property name="timeBetweenEvictionRunsMillis" value="1800000"></property>
            <property name="numTestsPerEvictionRun" value="3"></property>
            <property name="minEvictableIdleTimeMillis" value="1800000"></property>
            <property name="validationQuery" value="SELECT version()"></property>
        </bean>

Step 9 : Pushing code to Git

After making all changes mentioned above we are ready to push the code to OpenShift. Execute the command shown below.

git commit -am "made all necessary changes for supporting PostgreSQL database."
git push

Step 10 : Play with the application

After the Maven build runs and the deployment is complete, you can start booking hotels in the app. The source code with PostgreSQL changes is on github.

Could You provide example how to deploy war file?

The steps to deploy a war file are:
1) clone your repo
2) remove the pom.xml file from the root directory of your repo
3) add your war to the webapps/ dir of your repo
4) do git add/commit/push

For more details, please refer to the "README" file in the root of your git repo.

HTHs;
~Nam

excellent artice, as usual

keep them coming...

the application uses hsqldb and it works fine. I am trying another application which uses embedded hsql database, but it doesnt work as I keep getting the exception below. Please let me know in case there is some steps which i need to do in order to use hsqldb

[renergy-renergy.rhcloud.com hsql]> java -cp hsqldb-2.2.4.jar org.hsqldb.Server -database.0 file:broadleaf -dbname.0 broadleaf [Server@15e8a8f]: [Thread[main,5,main]]: checkRunning(false) entered [Server@15e8a8f]: [Thread[main,5,main]]: checkRunning(false) exited [Server@15e8a8f]: Startup sequence initiated from main() method [Server@15e8a8f]: Could not load properties from file [Server@15e8a8f]: Using cli/default properties only [Server@15e8a8f]: Initiating startup sequence... java.net.BindException: Permission denied at java.net.PlainSocketImpl.socketBind(Native Method) at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:376) at java.net.ServerSocket.bind(ServerSocket.java:376) at java.net.ServerSocket.(ServerSocket.java:237) at java.net.ServerSocket.(ServerSocket.java:128) at org.hsqldb.server.HsqlSocketFactory.createServerSocket(Unknown Source) at org.hsqldb.server.Server.openServerSocket(Unknown Source) at org.hsqldb.server.Server.run(Unknown Source) at org.hsqldb.server.Server.access$000(Unknown Source) at org.hsqldb.server.Server$ServerThread.run(Unknown Source) [Server@15e8a8f]: Initiating shutdown sequence... [Server@15e8a8f]: Shutdown sequence completed in 105 ms. [Server@15e8a8f]: 2013-02-26 09:57:55.593 SHUTDOWN : System.exit() is called next

You are trying to bind to a IP address and/or port that you are not authorized to use (java.net.BindException: Permission denied). Try to configure your db to $OPENSHIFT_INTERNAL_IP and $OPENSHIFT_INTERNAL_PORT

Reference: https://www.openshift.com/page/openshift-environment-variables

This is how you will run hsql in server mode

java -cp hsqldb.jar org.hsqldb.Server -database.0 file:mydb -dbname.0 xdb -port 15001 -address $OPENSHIFT_INTERNAL_IP

I was following this thread - where the travel application also uses hsqldb - and this work fine for me.

The post Reference: https://www.openshift.com/page/openshift-environment-variables, does not say any thing around what do I need to do and where in order to start hsqldb on my vm.

I just noticed the travel application in this page also uses hsql database - but i am not sure how the developer has managed to change it, as it works for me - without any other settings.

Regards Om

This blog goes over how to switch to postgresql (see persistence.xml). If you're still interested in using hsqldb, post your data.xml as the IP and port configurations would probably need some modification from defaults.

This right, the document does go about explaining how to move to postgresql, but it does work with hsql as , I have launched a version of the same application - { pre postgresql} and it works fine.

my data.xml is very similar to data.xml

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
    <property name="url" value="jdbc\:hsqldb\:mem\:mydb" />  
    <property name="username" value="sa" />
    <property name="password" value="" />
</property></property></property></property></bean>
   <jdbc:embedded-database id="embeddedDatasource" type="HSQL" />

<bean
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    <property name="dataSource" ref="dataSource" />
</property></bean>

Nice work! Glad it's working out.

Hello Nam, I meant its not working.

Hi,

It's a great article! I'm trying to deploy a spring mvc3 application for 3days. Now it worked! Thank you for that!

But I have a problem: when I'm trying to move on postgresql database, nothing happens! I'm mean: blank page. When I switch back, all back to normal! This happens with travel application too! Don't know what the problem is!

I followed these steps, but still shows me nothing!

Any idea why?

Check the logs files for any errors. here are the instructions: https://www.openshift.com/faq/how-to-troubleshoot-application-issues-using-logs

Hi, I have been trying to deploy a demo ecommerce site based on spring MVC framework. It works nicely with postgresql db with very minimum configuration. The only problem I have which I consider a show stopper is when the app redirects from http to https.

I tried to test another app, the official Spring security sample, and the same problem happened. I am using the default tomcat configuration and every time I access the page which requires secure channel, the following occurs

The webpage at https://test-broadleaf.rhcloud.com/secure/extreme/index.jsp has resulted in too many redirects. Clearing your cookies for this site or allowing third-party cookies may fix the problem. If not, it is possibly a server configuration issue and not a problem with your computer.

When I checked the logs, it looks like the redirection ended at the same, non secure, internal port 8080.

08:42:04.199 [http-bio-127.9.168.129-8080-exec-9] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/secure/extreme/index.jsp'; against '/loggedout.jsp'
08:42:04.222 [http-bio-127.9.168.129-8080-exec-9] DEBUG o.s.security.web.FilterChainProxy - /secure/extreme/index.jsp at position 1 of 13 in additional filter chain; firing Filter: 'ChannelProcessingFilter'
08:42:04.223 [http-bio-127.9.168.129-8080-exec-9] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/secure/extreme/index.jsp'; against '/secure/extreme/**'
08:42:04.223 [http-bio-127.9.168.129-8080-exec-9] DEBUG o.s.s.w.a.c.ChannelProcessingFilter - Request: FilterInvocation: URL: /secure/extreme/index.jsp; ConfigAttributes: [REQUIRES_SECURE_CHANNEL]
08:42:04.223 [http-bio-127.9.168.129-8080-exec-9] DEBUG o.s.s.w.a.c.RetryWithHttpsEntryPoint - Redirecting to: https://test-broadleaf.rhcloud.com/secure/extreme/index.jsp
08:42:04.223 [http-bio-127.9.168.129-8080-exec-9] DEBUG o.s.s.web.DefaultRedirectStrategy - Redirecting to 'https://test-broadleaf.rhcloud.com/secure/extreme/index.jsp'
08:42:04.522 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/secure/extreme/index.jsp'; against '/static/**'
08:42:04.523 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/secure/extreme/index.jsp'; against '/loggedout.jsp'
08:42:04.523 [http-bio-127.9.168.129-8080-exec-10] INFO  Spring Security Debugger - 
 
************************************************************
 
Request received for '/secure/extreme/index.jsp':
 
org.apache.catalina.connector.RequestFacade@8c337c
 
servletPath:/secure/extreme/index.jsp
pathInfo:null
 
Security filter chain: [
  ChannelProcessingFilter
  ConcurrentSessionFilter
  SecurityContextPersistenceFilter
  LogoutFilter
  UsernamePasswordAuthenticationFilter
  DefaultLoginPageGeneratingFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  RememberMeAuthenticationFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]
 
 
************************************************************
 
 
08:42:04.523 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/secure/extreme/index.jsp'; against '/static/**'
08:42:05.870 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/secure/extreme/index.jsp'; against '/loggedout.jsp'
08:42:05.881 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.security.web.FilterChainProxy - /secure/extreme/index.jsp at position 1 of 13 in additional filter chain; firing Filter: 'ChannelProcessingFilter'
08:42:05.882 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/secure/extreme/index.jsp'; against '/secure/extreme/**'
08:42:05.882 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.s.w.a.c.ChannelProcessingFilter - Request: FilterInvocation: URL: /secure/extreme/index.jsp; ConfigAttributes: [REQUIRES_SECURE_CHANNEL]
08:42:05.882 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.s.w.a.c.RetryWithHttpsEntryPoint - Redirecting to: https://test-broadleaf.rhcloud.com/secure/extreme/index.jsp
08:42:05.882 [http-bio-127.9.168.129-8080-exec-10] DEBUG o.s.s.web.DefaultRedirectStrategy - Redirecting to 'https://test-broadleaf.rhcloud.com/secure/extreme/index.jsp'
 
==> jbossews-2.0/logs/localhost_access_log.2013-06-06.txt <==
127.9.168.129 - - [06/Jun/2013:08:41:59 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:41:59 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:00 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:00 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:00 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:00 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:01 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:01 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:01 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:02 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:02 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:04 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -
127.9.168.129 - - [06/Jun/2013:08:42:05 -0400] "GET /secure/extreme/index.jsp HTTP/1.1" 302 -

I am thinking that the app requires separate internal port, secure and non secure, for this to work properly. Really appreaciate if you can help me with workaround.

Cheers,

Be aware of your alternatives as there are several ways to connect to a DB within Tomcat see https://access.redhat.com/site/solutions/464253 for more details.

What if you have a parent pom with one or more child modules and the war file(s) are generated by the child modules? How do you get the wars deployed? Thanks!