Deploying Tomcat Server in Cloud foundry


Apache Tomcat, often referred to as Tomcat, is an open-source web server developed by the Apache Software Foundation. Tomcat implements several Java EE specifications including Java Servlet, JavaServer Pages (JSP), Java EL, and WebSocket, and provides a "pure Java" HTTP web server environment for Java code to run in. My expectation though this blog post is to share my experience of deploying Apache Tomcat server in Cloud foundry. This would be useful for any one trying to run non-Web applications in Cloud Foundry, including application servers. 

This blog post will walk you through the steps to deploy a “hello world” application in a Tomcat 7 container on Cloud Foundry. 

The basic outline involves installing your application into a local Tomcat 7 instance, making minor modifications to the configuration and pushing the entire contents of Tomcat 7 and your application to Cloud Foundry as a standalone application.

I will provide you a step by step guide. There are four sections in this blog post. In the first section, Linux and OSX users should be able to test the scripts on their local systems before pushing them to Cloud Foundry’s Ubuntu-based server environment. Unfortunately, Windows users cannot test the bin/startup.sh script changes locally first. So the Section2 is for Linux and OSX users. Any one can skip that section and proceed with Section3 and section4 after Section1.

Section1

Step1:Download Apache Tomcat

Download Apache Tomcat 7 and extract it to your preferred location. I extracted it to my home directory.  In my case, I downloaded tar.gz under Core in Binary distributions section. All of the commands throughout this blog post will assume that the present working directory is the Tomcat 7 base directory.


Step2: Update permissions

I changed the permissions of the bin/*.sh scripts to have executable permissions.

cd apache-tomcat-7.0.27 
$ chmod +x bin/*.sh

Section2

Step1: Edit Startup Scripts

Navigate to the following scripts and do the relevant modifications.

bin/startup.sh

Tomcat is typically started with the bin/startup.sh script. In order for Tomcat to use the same shell that invokes startup.sh instead of spawning a new shell, change the execution argument in the last line of startup.sh from “start” to “run“:

exec "$PRGDIR"/"$EXECUTABLE" start "$@"
to:

exec "$PRGDIR"/"$EXECUTABLE" run "$@"


bin/catalina.sh

Instead of using a pre-defined static port, we would like Tomcat 7 to use the port assigned by Cloud Foundry, which will be stored in the VCAP_APP_PORT environment variable when deployed. Place the following bash code near the top of catalina.sh after the initial comments. Then we can run this locally as well without modifying the code, this code will assign a static port number of 8080 if the dynamic port is not available as an environment variable.

# USE VCAP PORT IF IT EXISTS, OTHERWISE DEFAULT TO 8080
if [ -z ${VCAP_APP_PORT} ]; then
export VCAP_APP_PORT=8080
fi
export JAVA_OPTS="-Dport.http.nonssl=$VCAP_APP_PORT $JAVA_OPTS"


Step2: Edit Tomcat configuration

Navigate to the following scripts and do the relevant modifications.

conf/server.xml

Set the port attribute of the Server element to -1, which disables the Tomcat shutdown port.

We want to avoid any potential port conflicts with other applications that are running on the same Droplet Execution Agent (DEA), so only using a single http port is the current recommendation for standalone applications running on Cloud Foundry.

Server port=”-1” command=”SHUTDOWN”

Since Cloud Foundry handles the load balancing for you without using the AJP connector, you should disable the AJP connector to ensure we do not get a port conflict by commenting out the section shown below.

  Define an AJP 1.3 Connector on port 8009

The Connector element should use the port provided in the JAVA_OPTS environment variable, which we have set previously in the catalina.sh script.

Connector port="${port.http.nonssl}" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"

At this point, I recommend trying the edits on your local server to see if Tomcat 7 starts up as expected. In order to test whether the VCAP_APP_PORT is being used, I recommend using a command shell to assign a sample port such as 8082.

$ export VCAP_APP_PORT=8082


Step3: Run the server

$ sh bin/startup.sh
Then the console should return something like this.

nanduni @ nanduni-TECRA-M11: ~ / apache-tomcat-7.0.67 $ bin / startup.sh
Using CATALINA_BASE:   /home/nanduni/apache-tomcat-7.0.67
Using CATALINA_HOME:   /home/nanduni/apache-tomcat-7.0.67
Using CATALINA_TMPDIR: /home/nanduni/apache-tomcat-7.0.67/temp
Using JRE_HOME:        /usr/lib/jvm/java-8-oracle
Using CLASSPATH:       /home/nanduni/apache-tomcat-7.0.67/bin/bootstrap.jar:/home/nanduni/apache-tomcat-7.0.67/bin/tomcat-juli.jar
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version:        Apache Tomcat/7.0.67
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server built:          Dec 7 2015 13:07:11 UTC
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server number:         7.0.67.0
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Name:               Linux
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Version:            3.13.0-76-generic
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Architecture:          amd64
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Java Home:             /usr/lib/jvm/java-8-oracle/jre
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Version:           1.8.0_72-b15
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Vendor:            Oracle Corporation
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_BASE:         /home/nanduni/apache-tomcat-7.0.67
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_HOME:         /home/nanduni/apache-tomcat-7.0.67
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.util.logging.config.file=/home/nanduni/apache-tomcat-7.0.67/conf/logging.properties
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dport.http.nonssl=8080
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.endorsed.dirs=/home/nanduni/apache-tomcat-7.0.67/endorsed
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.base=/home/nanduni/apache-tomcat-7.0.67
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.home=/home/nanduni/apache-tomcat-7.0.67
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.io.tmpdir=/home/nanduni/apache-tomcat-7.0.67/temp
Jan 29, 2016 5:04:53 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Jan 29, 2016 5:04:53 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 2157 ms
January 29, 2016 5:04:53 PM org.apache.catalina.core.StandardService start internalize
INFO: Starting service Catalina
Jan 29, 2016 5:04:53 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.67
Jan 29, 2016 5:04:53 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/Servlet3
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/Servlet3 has finished in 1,066 ms
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/ROOT
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/ROOT has finished in 165 ms
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/host-manager
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/host-manager has finished in 89 ms
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/docs
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/docs has finished in 137 ms
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/manager
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/manager has finished in 74 ms
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/examples
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/nanduni/apache-tomcat-7.0.67/webapps/examples has finished in 364 ms
Jan 29, 2016 5:04:55 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Jan 29, 2016 5:04:55 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 2151 ms

If that is the case, you should be able to visit http://localhost:8082 to see if the welcome page is there.



Section3 : Create an application


Now let's move to the interesting part. Deploying Tomcat server in cloud foundry. By this time, I suppose that you gave cf CLI tool installed in your machine. If not go through this blog post. I will be using cf CLI tool to push my apps to cloud foundry. You may use other tools like VMC too.

As a first step, let's create an application to run in Tomcat server. I will be creating a servlet.

1. Navigate to the apache-tomcat-7.0.67/webapps/ROOT/WEB-INF folder. This is because that Cloud Foundry uses the ROOT web application. 

2. Create a folder called classes inside this WEB-INF folder.

3. Create a folder called tomcat7 inside this classes folder.

So the folder structure will look like this.

apache-tomcat-7.0.67 
                     |__webapps  
                                 |__  ROOT
                                             |__  WEB-INF
                                                          |__  classes     ,     web.xml
                                                                       |__  tomcat7
                                                                                    |__  Servlet3.java

4. Create a java file inside it and add the following content to it. 

package tomcat7;
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*;
@WebServlet("/Servlet3") public class Servlet3 extends HttpServlet {
    public Servlet3() {
        super();
    }
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().println("Hello from Servlet 3.0!");
    }
}

5. Navigate to the tomcat7 directory from command prompt and use this command to compile the program.

nanduni @ nanduni-TECRA-M11: ~ / apache-tomcat-7.0.67 / webapps / ROOT / tomcat7 $ javac -cp Servlet3.java

If there are no errors, the program will be compiled successfully, otherwise correct the errors and compile again. If the compilation is successful, Servlet3.class will be created inside tomcat7 directory. 

6. Now we need to edit the web.xml file inside WEB-INF directory. Add the content in red, bold text to the file.



<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0  (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and 
limitations under the License.
-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
               http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="true"><display-name>Welcome to Tomcat</display-name><description>Welcome to Tomcat</description>
<servlet>
      <servlet-name>Example</servlet-name>     
      <servlet-class>tomcat7.Servlet3</servlet-class> </servlet>
<servlet-mapping>
      <servlet-name>Example</servlet-name>
      <url-pattern>/servlets/servlet/example</url-pattern>
</servlet-mapping>
</web-app>


7. If you have started the server, Linux and OSX users can check the application working locally by the following url. 
localhost:8082/servlets/servlet/example


Now creating and installing the application is almost successful. Let's deploy the application in Cloud foundry.


Section3 : Push the application to Cloud Foundry

Execute the cf push command from the ROOT directory. You can give any unique name for the application with push command. I gave 'mytomcat' name. 

If the pushing is successful, output would look like this.
nanduni @ nanduni-TECRA-M11: ~ / apache-tomcat-7.0.67 / webapps / ROOT $ cf push mytomcat
Creating app mytomcat in org nanduni-org / space development as nanduni@wso2.com...
OK
Using route mytomcat.cfapps.io
Binding mytomcat.cfapps.io to mytomcat...
OK
Uploading mytomcat...
Uploading app files from: /home/nanduni/apache-tomcat-7.0.67/webapps/ROOT
Uploading 98.2K, 23 files
Done uploading               
OK
Starting app mytomcat in org nanduni-org / space development as nanduni@wso2.com...
Downloading staticfile_buildpack...
Downloading java_buildpack...
Downloading ruby_buildpack...
Downloading nodejs_buildpack...
Downloading go_buildpack...
Downloading python_buildpack...
Downloading php_buildpack...
Downloading liberty_buildpack...
Downloading binary_buildpack...
Downloaded java_buildpack
Downloaded staticfile_buildpack
Downloaded php_buildpack
Downloaded ruby_buildpack
Downloaded python_buildpack
Downloaded liberty_buildpack
Downloaded nodejs_buildpack
Downloaded binary_buildpack
Downloaded go_buildpack
Creating container
Successfully created container
Downloading app package...
Downloaded app package (106K)
Staging...
-----> Java Buildpack Version: v3.5.1 | http://github.com/pivotal-cf/pcf-java-buildpack.git#d6c19f8
-----> Downloading Open Jdk JRE 1.8.0_71 from https://download.run.pivotal.io/openjdk/trusty/x86_64/openjdk-1.8.0_71.tar.gz (0.8s)
       Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (1.3s)
-----> Downloading Open JDK Like Memory Calculator 2.0.1_RELEASE from https://download.run.pivotal.io/memory-calculator/trusty/x86_64/memory-calculator-2.0.1_RELEASE.tar.gz (0.0s)
-----> Downloading Tomcat Instance 8.0.30 from https://download.run.pivotal.io/tomcat/tomcat-8.0.30.tar.gz (0.2s)
       Expanding Tomcat Instance to .java-buildpack/tomcat (0.1s)
-----> Downloading Tomcat Lifecycle Support 2.5.0_RELEASE from https://download.run.pivotal.io/tomcat-lifecycle-support/tomcat-lifecycle-support-2.5.0_RELEASE.jar (0.0s)
-----> Downloading Tomcat Logging Support 2.5.0_RELEASE from https://download.run.pivotal.io/tomcat-logging-support/tomcat-logging-support-2.5.0_RELEASE.jar (0.0s)
-----> Downloading Tomcat Access Logging Support 2.5.0_RELEASE from https://download.run.pivotal.io/tomcat-access-logging-support/tomcat-access-logging-support-2.5.0_RELEASE.jar (0.0s)
Exit status 0
Staging complete
Uploading droplet, build artifacts cache...
Uploading droplet...
Uploading build artifacts cache...
Uploaded build artifacts cache (52.3M)
Uploaded droplet (51.2M)
Uploading complete
1 of 1 instances running
App started
OK
App mytomcat was started using this command `CALCULATED_MEMORY=$($PWD/.java-buildpack/open_jdk_jre/bin/java-buildpack-memory-calculator-2.0.1_RELEASE -memorySizes=metaspace:64m.. -memoryWeights=heap:75,metaspace:10,native:10,stack:5 -memoryInitials=heap:100%,metaspace:100% -totMemory=$MEMORY_LIMIT) &&  JAVA_HOME=$PWD/.java-buildpack/open_jdk_jre JAVA_OPTS="-Djava.io.tmpdir=$TMPDIR -XX:OnOutOfMemoryError=$PWD/.java-buildpack/open_jdk_jre/bin/killjava.sh $CALCULATED_MEMORY -Daccess.logging.enabled=false -Dhttp.port=$PORT" exec $PWD/.java-buildpack/tomcat/bin/catalina.sh run`
Showing health and status for app mytomcat in org nanduni-org / space development as nanduni@wso2.com...
OK
requested state: started
instances: 1/1
usage: 1G x 1 instances
urls: mytomcat.cfapps.io
last uploaded: Fri Jan 29 06:53:09 UTC 2016
stack: cflinuxfs2
buildpack: java-buildpack=v3.5.1-http://github.com/pivotal-cf/pcf-java-buildpack.git#d6c19f8 open-jdk-like-jre=1.8.0_71 open-jdk-like-memory-calculator=2.0.1_RELEASE tomcat-access-logging-support=2.5.0_RELEASE tomcat-instance=8.0.30 tomcat-lifecycle-support=2.5.0...
     state     since                    cpu    memory    disk      details   
#0   running   2016-01-29 12:23:45 PM   0.0%   0 of 1G   0 of 1G      


Following describes in brief what happens when you push an application to Cloud Foundry.

When you push the application to Cloud Foundry, first it creates an application by the name you give. The application belongs to your organization and your space. If your pushing the application not for the first time, it will update the application.
Application files are then uploaded.
After successfully uploading the app files to Cloud Foundry, it starts the application inside that particular organization and space.
Then the available buildpacks are downloaded.
Then the container for the application is created.
After successfully creating the container, app package and build artifacts are downloaded.
Then the application is staged.
Then the droplet and build artifacts are uploaded.
Application starts when the uploading is completed.
Then it will show the health of the application.
Finally it will show the details of the state, url, instances, buildpack that detected the application, CPU, memory etc.


If your application does not match with any buildpacks, it will display the following message. So make sure to navigate to the relevant directory that contains your application before pushing.


None of the buildpacks detected a compatible application
Exit status 222
Staging failed: Exited with status 222
FAILED
NoAppDetectedError

Now you can try to deploy applications in other servers too. Most applications should be able to use the existing frameworks Cloud Foundry makes available.



3 comments :

  1. Hi,

    I am trying to deploy wso2 api manager to cloudfoundry, I created a buildpack including the wso2 api manager, I am able to start the tomcat server and listen to http port, the https redirect doesn't work, a infinte redirect happens, when accessing https only urls like publisher, Do you have any idea on this?

    -Regards
    -Prakash

    ReplyDelete
    Replies
    1. Hi, I suppose it is because that Cloud Foundry does not support https yet. ssl is terminated at the go router. Actually the gorouter expects either SSL to be termnated at the load balancer in front of its instances or it can terminate itself. So we are not able to access the management consle via 9443 port which is an HTTPS port. How did you configure the ports? Sometimes it may be because of a problem in the compile or release script of the buildpack too. What type of a buildpack have you built?

      Delete
  2. This comment has been removed by the author.

    ReplyDelete