Skip to content

Tomcat

Sorry, but you will fail to find much structure in this document. It is all kinds of tomcat findings and problems thrown together.

Note that all kinds of tomcat versions are used tomcat6/tomcat7/tomcat8. I leave those in because I know the section worked for that version.

As of July 2017, i focus on tomcat8 ! As of 2022 tomcat9

Setup

Add these lines to the file : /etc/tomcat8/tomcat-users.xml

/etc/tomcat8/tomcat-users.xml
<role rolename="manager"/> <user username="tomcat" password="s3cret" roles="manager-gui,manager-script,manager-jmx,manager-status"/>
  • manager-gui is for the management interface
  • manager-status is for the server status page only
  • manager-script is for plain text tools interface and the status page
  • manager-jmx is for the jmx proxy interface and the status page

Just add all. This is the only line needed to get started, no extra roles or users.

of course, change at will with your own username and pass, then :

restart
/etc/init.d/tomcat6 restart

Logging

Ok, tired of not seeing a bloody thing in the logs accept GET /hello/ let's set EVERYTHING ON !!

/etc/tomcat7/logging.properties
echo ".level = FINEST" >> /etc/tomcat7/logging.properties
service tomcat7 restart

FINALLY i see something happening when running :

catalina
tail -f /var/log/tomcat7/catalina.out

Catalina.out is the logfile for the output of the main 'executable' tomcat itself. It will probably be your best guess. There is also /var/log/tomcat7/localhost-date.log that you might want top watch.

Let's start with ... problems :

autoDeploy

Next problem is the constant stream of messages about redeploying all servlets. It does that every 10 seconds and it clogs up the logs if you want it set to FINEST. There are in fact 2 solutions to this, disable autoDeploy or set the loglevel to FINE. See above for the second solution.

Edit /etc/tomcat7/server.xml and search for autoDeploy. Set it to "false".

404 : description The requested resource (/hello) is not available.

This is fantastically poorly documented. So here is what worked and what did not. A servlet with no package to keep it simple : ServletDemo.java :

simple servlet
import java.io.IOException; import java.io.PrintWriter; import
javax.servlet.http.HttpServlet; import
javax.servlet.http.HttpServletRequest; import
javax.servlet.http.HttpServletResponse;

public class ServletDemo extends HttpServlet{
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException{
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<h1>Hello Klopt Servlet Get</h1>");
        out.println("</body>");
        out.println("</html>");
    }

}

So the class will be ServletDemo(.class) and the mapping should be set in webapps/hello/WEB-IBNF/web.xml :

webapps/hello/WEB-IBNF/web.xml
1
2
3
4
5
6
7
8
9
<servlet>
    <servlet-name>Servlet Name For Demo</servlet-name>
    <servlet-class>ServletDemo</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>Servlet Name For Demo</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

Hoping this would work on url http://localhost:8080/hello it will not... it works on http://localhost:8080/hello/hello

So map a servlet-class with a url-pattern by matching the 'servlet-name' tag. Then you will have these combinations work/fail :

/hello

only works with :

working url
http://localhost:8080/hello/hello

All these will fail :

failing
1
2
3
http://localhost:8080/hello/hello/
http://localhost:8080/hello
http://localhost:8080/hello/ServletDemo

However, using this url :

/

Will work with all of the above/below :

all working
1
2
3
4
5
http://localhost:8080/hello/hello/
http://localhost:8080/hello
http://localhost:8080/hello/ServletDemo
http://localhost:8080/hello
http://localhost:8080/hello/whatever
except all the others
http://localhost:8080/anythingelse

To make it simple, use the latter, since it is the only format that will let your deployed file work with the tomcat interface itself. Notice that it will say /hello. So it will give a 404 error if you click on it unless you use '/'

directory tree

This tree is enough to get the above example running :

directory tree
hello
└── WEB-INF
    ├── classes
    │   └── ServletDemo.class
    ├── lib
    │   └── servlet-api.jar
    └── web.xml
To deploy it with ant, use this template, which also builds the class the build.xml file below is used, however since it is smaller i will start with the build.properties file that is used by it :

build.properties
project-name=hello
builder=kees klop
tomcat-manager-url=http://localhost:8080/manager/text
tomcat-manager-username=tomcat
apache


Remember that tomcat operates on port 8080, and apache is on port 80. Just placing calls from apache javascript to tomcat will not work because it is regarded another url : x.x.x.x:80 -> x.x.x.x:8080 is just as forbidden as x.x.x.x:80 -> y.y.y.y:80

So you need to setup tomcat as an apache proxy to smooth that out, add a file called tomcat to /etc/apache2/conf.d with these contents:

```apache html title="config" linenums="1" LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so LoadModule proxy_ftp_module modules/mod_proxy_ftp.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_connect_module modules/mod_proxy_connect.so ProxyPass /bag http://localhost:8080/bag ProxyPassReverse /bag http://localhost:8080/bag

Restart the apache server:

```html title="restart" linenums="1"
/etc/init.d/apache2 restart

Now these should give the same result

same result
1
2
3
http://localhost:8080/bag
http://localhost:80/bag
http://localhost/bag

If you get this error.

error
No protocol handler was valid for the URL /bag. If you are using a DSO version of mod_proxy, make sure the proxy submodules are included in the configuration using LoadModule.

It means you forgot the other two modules as described above.

add
1
2
3
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so

I mention this because the tutorial i used did not mention those modules.

setup ant

If not already installed :

install ant
apt-get install ant

And to take advantage of some ant tasks provides by tomcat, you need to make catalina-ant.jar reachable for ant so just copy it :

get jar file
sudo cp $CATALINE_HOME/lib/catalina-ant.jar $ANT_HOME/lib

You will have to find these directories yourself, in my case it was :

get jar file
sudo cp /usr/share/tomcat6/lib/catalina-ant.jar /usr/share/ant/lib/

development

Cannot skip to deployment until I have an application. So some hello world would be nice. I will be using ant, which has some aids in creating directories etc. But you will need a build.xml first.

build.xml
1
2
3
4
mkdir myproject
cd myproject
wget http://tomcat.apache.org/tomcat-7.0-doc/appdev/build.xml.txt
mv build.xml.txt build.xml

View the build.xml and most importantly search for the catalina.home setting, it has to be set to your installation. I found mine by looking in /etc/init.d/tomcat6 , where it turned out to be /usr/share/tomcat6. After that running

run
ant

Will create some basic build directories for you.

The basic layout of a project directory should be :

``` project structure projectdir src doc web WEB-INF

So create that with

``` title="create" linenums="1"
mkdir src
mkdir doc
mkdir web

I use the sample app as a base , so WEB-INF will be create automatically :

WEB-INF
cd web
wget http://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war 
jar -xvf sample.war

compilation

First set some properties needed for compilation and deployment. You can do that in the file build.properties alongside build.xml :

<pre>
 app.path=/hello
 # Tomcat 7 installation directory
 catalina.home=/usr/share/tomcat6
 # Manager webapp username and password
 manager.username=kees
 manager.password=][p][p
 build.sysclasspath=ignore
</pre>
The last entry is to shut-up the warning :

<pre>
 compile:
    [javac] /home/kees/projects/jsp/hello/build.xml:282: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
</pre>
Deployment


ant install
ant install

yep, that's it. the tomcat server's /text service knows where to put the war file with the data from build.properties. So look at http://localhost:8080/hello

after that however, ant install will complain about already being installed, docs say you should use reload for that :

<pre>
 ant reload
</pre>
But i cannot make that work for now. Here are the task you can use with ant :

<pre>
&lt;!-- Configure the custom Ant tasks for the Manager application --&gt;
  &lt;taskdef name="deploy"    classname="org.apache.catalina.ant.DeployTask"/&gt;
  &lt;taskdef name="install"   classname="org.apache.catalina.ant.InstallTask"/&gt;
  &lt;taskdef name="list"      classname="org.apache.catalina.ant.ListTask"/&gt;
  &lt;taskdef name="reload"    classname="org.apache.catalina.ant.ReloadTask"/&gt;
  &lt;taskdef name="remove"    classname="org.apache.catalina.ant.RemoveTask"/&gt;
  &lt;taskdef name="resources" classname="org.apache.catalina.ant.ResourcesTask"/&gt;
  &lt;taskdef name="roles"     classname="org.apache.catalina.ant.RolesTask"/&gt;
  &lt;taskdef name="start"     classname="org.apache.catalina.ant.StartTask"/&gt;
  &lt;taskdef name="stop"      classname="org.apache.catalina.ant.StopTask"/&gt;
  &lt;taskdef name="undeploy"  classname="org.apache.catalina.ant.UndeployTask"/&gt;
</pre>
So i now use :

ant remove ant install

because that seems to stick. For a shorthand, make this your entry in build.xml

build.xml
<target name="reload" depends="compile,remove,install">
</target>

Of course, rename the old reload target to "reloadbogus" or something.

Physically, your app is now copied to /var/lib/tomcat7/webapps :

<pre>
cd /var/lib/tomcat7
find .
</pre>
gives

<pre>
...
./webapps/hello
./webapps/hello/WEB-INF
./webapps/hello/WEB-INF/classes
./webapps/hello/WEB-INF/classes/mypackage
./webapps/hello/WEB-INF/classes/mypackage/Hello.class
./webapps/hello/WEB-INF/lib
./webapps/hello/WEB-INF/web.xml
./webapps/hello/images
./webapps/hello/images/tomcat.gif
./webapps/hello/hello.jsp
./webapps/hello/index.html
./webapps/hello/META-INF
./webapps/hello/META-INF/MANIFEST.MF
...
</pre>
Something you could also do by hand yourself as a legitimate way of deployment.

Development 2 (continued)

The first dev chapter was just to aid deployment, let's dig in some deeper here.

Servlets and jsp pages

jsp means javaserver pages, and they are html; pages that allow java code to be evaluated inside. Servlets are server side java applets, and actually jsp pages are converted to servlets : (source : http://en.wikipedia.org/wiki/JavaServer_Pages)

the .jsp page :

<pre>
 &lt;%@ page errorPage="myerror.jsp" %&gt;
 &lt;%@ page import="com.foo.bar" %&gt;

 &lt;html&gt;
 &lt;head&gt;
 &lt;%! int serverInstanceVariable = 1;%&gt;

 &lt;% int localStackBasedVariable = 1; %&gt;
 &lt;table&gt;
 &lt;tr&gt;&lt;td&gt;&lt;%= toStringOrBlank( "expanded inline data " + 1 ) %&gt;&lt;/td&gt;&lt;/tr&gt;
</pre>
becomes the servlet :

servlet code
package jsp_servlet;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

import com.foo.bar; // Imported as a result of &lt;%@ page import="com.foo.bar" %&gt;
import 

class _myservlet implements javax.servlet.Servlet, javax.servlet.jsp.HttpJspPage {
   // Inserted as a
   // result of &lt;%! int serverInstanceVariable = 1;%&gt;
   int serverInstanceVariable = 1;
   

   public void _jspService( javax.servlet.http.HttpServletRequest request,
   javax.servlet.http.HttpServletResponse response )
   throws javax.servlet.ServletException,
   java.io.IOException
   {
       javax.servlet.ServletConfig config = ; // Get the servlet config
       Object page = this;
       PageContext pageContext = ;            // Get the page context for this request
       javax.servlet.jsp.JspWriter out = pageContext.getOut();
       HttpSession session = request.getSession( true );
       try {
           out.print( "&lt;html&gt;rn" );
           out.print( "&lt;head&gt;rn" );
           // From &lt;% int localStackBasedVariable = 1; %&gt;
           int localStackBasedVariable = 1;
           
           out.print( "&lt;table&gt;rn" );
           out.print( " &lt;tr&gt;&lt;td&gt;" );
           // From &lt;%= toStringOrBlank( "expanded inline data " + 1 ) %&gt;
           out.print( toStringOrBlank( "expanded inline data " + 1 ) );
           out.print( " &lt;/td&gt;&lt;/tr&gt;rn" );
       } catch ( Exception _exception ) {
           // Clean up and redirect to error page in &lt;%@ page errorPage="myerror.jsp" %&gt;
       }
   }
}

troubleshooting

unable to startup,particularly port 8005

This is the port at with tomcat listen for shutdown !!. Nevertheless it will hang with :

shutdown hang
1
2
3
Jan 26, 2015 9:05:03 AM org.apache.catalina.core.StandardServer await
SEVERE: StandardServer.await: create[8005]: 
java.net.BindException: Cannot assign requested address

Check your /etc/hosts file !!!

This is NOT a problem with the port already being used, it is a problem with resolving localhost.

In my case i mistyped localhost :

wrong localhost
172.0.0.1 localhost

Where of course it should be

/etc/hosts
127.0.0.1 localhost

Debugging

tomcat preparation

This is for tomcat8 but the advise comes from tomcat7 docs. Lot's of advise about starting tomcat to listen on debug port 8000 focuses on the startup scripts, but that just does not startup by hand so here is a systemd version.

Edit /etc/default/tomcat8 and scroll to these lines:

/etc/default/tomcat8
1
2
3
# To enable remote debugging uncomment the following line.
# You will then be able to use a java debugger on port 8000.
#JAVA_OPTS="${JAVA_OPTS} -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"

Obviously uncomment the 3rd line, and then restart tomcat8 :

restart
systemctl restart tomcat8
telnet localhost 8000

You will get a connection, but you will need

jdb usage

An actual example for the backend project, which just printed :

HTTP Status 405 - Method Not Allowed

Thank you.. but this is a problem occurring earlier then jdb can be of any help.

so to see what is happening connect to tomcat with jdb :

debug
jdb -attach 8000

This will give you a prompt from where to start, you can list the threads to verify you are actually debugging tomcat :

list the threads
threads

Now you can issue a run command but it will say: "nothing suspended". Maybe forcing the error will help ?

intellij usage

Later...