Using the HP MediaVault as a Java Application Server

By Peter Michael Bruun

Contact: pmbruun@yahoo.com

See also: My Family Home

Overview of my MediaVault pages:


Why use the MV as a Java Application Server?

An "Application Server" is a framework for developing web-applications - as opposed to individual pages, as described under Using the HP MediaVault as a Web Server.

In fact, using PHP you can achieve almost anything, so the need for an advanced application server only arises, if PHP becomes unsuited due to the complexity of the application you want to create, and so you want to apply real software development methods to your web application.

So if you are an experienced Java developer, then this page may interest you.

If not, then you are highly unlikely to benefit from the contents of this page.

Having said that, the MV is running a Jetty 5.1.2 server - mainly for the purpose of supporting the Photo Webshare application. This environment is "easily" extensible to support other Java servlets or (pre-compiled) JSP pages. Of course you should remember the limited CPU power of the MV - highly CPU intensive applications are not likely to work well.

Before you start: BE WARNED

This page describes my personal experience with the HP MediaVault, and implies no liability and no endorsement or support by HP, whatsoever.

Following these instructions is likely to void any warranty on the HP MediaVault.

Before continuing here, you should already have successfully turned your HP MediaVault into a Web Server following my instructions.

A Note on the J2EE Application Server Standard

The J2EE standard is the standardized parallel to Microsoft's .NET standard. It is a framework for developing Web applications based on Enterprise Server Beans, and a J2EE server offers a lot of facilities to the application developer - persistence, transactions, etc.

Jetty is designed to be embedded in a full-fledged J2EE Application Server, and so it follows the J2EE standard directory layout for Application Servers. Jetty alone is "just" a Java servlet container. If you are looking for a full Open Source J2EE implementation, you could look at JBoss, Geronimo, or JOnAS, but I doubt that the MV would scale adequately to support any of them.

If you are not familiar with the J2EE standard and Java Servlets, I suggest you read a brief tutorial such as this Basic Introduction to Java Servlets.

One thing that typically confounds the beginner is the plethora of XML configuration files and the rather complex directory structure (a small subset is explained here). Also, see the Jetty documentation.

The design paradigm is intended to allow as much as possible to be done by "configuration" and as little as possible by "coding".

The argument for the distinction is that work to be done by highly skilled software developers can be separated from less-skilled work to be performed by configuration.

The result is that there are XML formats for configuring anything and everything in J2EE. In my personal opinion, the complexity and required skills of these configurations is at least as high as that of code, and usually, there is a very tight coupling between the application code and the accompanying configuration file contents. So I see no real benefit from the separation.

Of course the J2EE standard provides a lot of functionality "below the hood" - it is like an entire operating system - offering a lot of functionality to the application delveloper. So now it is just to be accepted the way it is.

Preparations for setting up a new Web Application in Jetty

I recommend you go through the following steps:

Create the Structure for your new Application in the Jetty Server

I chose the name "mvservlet", so on the MV, I created the following directories:
  /usr/jetty-5.1.12/webapps/mvservlet
  /usr/jetty-5.1.12/webapps/mvservlet/WEB-INF
  /usr/jetty-5.1.12/webapps/mvservlet/WEB-INF/lib
  /usr/jetty-5.1.12/webapps/mvservlet/WEB-INF/classes
The WEB-INF directory is where the meta-data and compiled code for you application is stored. You must create a configuration file named web.xml in the WEB-INF directory. My web.xml file has the following contents:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app 
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
         "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="MVServlet">
  <display-name>Java Servlet Application</display-name>
  <servlet>
    <servlet-name>HelloWorldServlet</servlet-name>
    <servlet-class>dk.hosbruun.mvservlet.hello.HelloWorldServlet</servlet-class>
    <init-param>
      <param-name>dbname</param-name>
      <param-value>mydb</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloWorldServlet</servlet-name>
    <url-pattern>hello/*</url-pattern>
  </servlet-mapping>
</web-app>
The url-pattern "hello/*" means that all requests for URLs matching that pattern will be processed by a class named HelloWorldServlet.java which I wrote. You can no longer try the servlet, as my provider has stopped offering fixed-IP addresses to non-business customers. In the web.xml you can decide any parameter name-value pairs that you will want Jetty to pass to your application upon startup.

Register you Application in the Jetty server configuration

You must edit the following file - either using the vi editor natively from a prompt or by copying the file to a Samba share directory and editing it using a Windows edtior. (Note that XML files do not care about line-endings, so you do not have to convert the file to Unix format after editing on Windows.)
  /usr/jetty-5.1.12/etc/mediasmart.xml
Remember to create a backup of the file, before you edit it.

In this file, locate the section named "Add jetty MediaSmartUpdate web application", and create a similar section for your own application below it. It may look something like this:

  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
  <!-- Add jetty mvservlet web application.                    -->
  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
  <Call name="addWebApplication">
    <Arg>/mvservlet/*</Arg>
    <Arg><SystemProperty name="jetty.home" default="."/>/webapps/mvservlet</Arg>
    <Set name="extractWAR">false</Set> 
    <Set name="classLoaderJava2Compliant">false</Set>
  </Call>
The <!-- --> sections are XML comments.

What all the other incantations mean, I do not know, and frankly, I do not want to know - this is "just" magic.

Tell Apache to delegate to your application on Jetty

The Apache server is the main web-server on the MediaVault, but the pages of your Java application will be served by the Jetty application server, which is running as a separate process.

In order to achieve this, the Apache server must be configured to understand which request patterns it should delegate to Jetty.

Following the instructions in Prerequisites for Hacking the HP MediaVault you must edit the file /etc/inc/func_httpd.inc

Remember to convert to Unix format using the dos2unix tool if you edit the file on Windows.

Warning: Any mistakes in this step will most likely "brick" your MV.

In the existing section of JKMount lines, I added the following:

  JKMount /mvservlet/* ajp13
The choice of patterns you want to map to Jetty is of course up to you. Jetty also works as a "normal" web-server, and so the above pattern will allow access to ordinary .html files and even directories - but not the directory named "WEB-INF". I therefore strongly recommend that you place a default HTML page named "index.html" in all the application directories to prevent directory-level browsing.

Create a script for re-starting Jetty

You don't want to have to re-boot the MV every time you change your application, so you need a handy script for re-starting the Jetty server independently of Apache.

I created a script file named /usr/jetty-5.1.12/restart_jetty.sh with the following contents:

#!/bin/sh
/usr/bin/killall jamvm
sleep 1
HP_CONF_FILE="/etc/Server.ini"
JETTY_CONF="/usr/jetty-5.1.12/etc/mediasmart.xml"
jamvm -jar -DHP_CONF_FILE=$HP_CONF_FILE -Djetty.class.path=lib/ajpFix.jar start.jar $JETTY_CONF &
It is critical, that the file does not have Windows line-endings. If you create it from Windows, use the dos2unix tool as described in Prerequisites for Hacking the HP MediaVault.

In order to make the script executable, you must run the command:

  chmod +x /usr/jetty-5.1.12/restart_jetty.sh
Now Jetty can be re-started by typing:
  /usr/jetty-5.1.12/restart_jetty.sh
When you use the script, Jetty will output a lot of boring details, then it pauses for a minute or two, before finally loading the applications.

If you attempt to use your application or the Photo Webshare application too early, Apache will give you the following message:

   Service Temporarily Unavailable
   The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

Do not be too impatient - as mentioned, Jetty takes quite a while to come back completely.

But of course, if you mess up its configuration files, particularly the mediasmart.xml, Jetty may not come back at all.

Luckily, such an error should not brick your MV - only the Photo WebShare application would be affected, so it will be easy enough to restore the mediasmart.xml, provided you remembered to take a backup.

Create and deploy the Hello World application.

Now we are ready to work with Java.

Getting a Java Development Environment

The MV does not come equipped with a Java SDK - it only has a "jamvm" Java run-time engine, which is special for small environments, and presumably has some limitations.

The jamvm runtime-engine is compatible with Java compiler version 1.4.2.

As there is no java compiler on the Media Vault, all java classes must be cross-compiled on a separate computer - typically Windows - where you have downloaded and installed the Java SDK from Sun.

You could also experiment with installing the Java SDK using the Linux package manager, but for these pages, I have made a point of using only the tools that are already on the MV as shipped from HP.

To get the Java 1.4.2 SDK (free), go to this site and choose the "Download J2SE SDK" link.

You will be asked to fill in your name etc. to be allowed to download.

Java 1.4.2 was one of the most stable and widely used versions of Java, but Sun has declared its "End of Service Life" by October, 2008. This means that Sun's download pages will complain about your download and attempt to re-direct you to a later version.

You must insist on getting version 1.4.2 (highest patch-level), and accept that Sun now imposes some restrictions on getting it.

Once you have the Java 1.4.2 SDK, I suggest you select the option to download the full version. This will enable you to deploy on all your current or future PCs, as getting version 1.4.2 may get increasingly difficult over time.

For developing your Java application, you may want to use an Integrated Development Environment (IDE). As you are reading this page, I assume that you already have a favourite environment, but in case you do not, then I suggest you download the Eclipse environment. Eclipse is a free and professional grade IDE.

Remember to select the Java 1.4.2 compiler and run-time for your IDE (Eclipse) project.

Writing the HelloWorld Servlet

Now you should be ready to create your servlet class.

Remember, that the servlet class name - fully qualified with the package name - was referenced from your web.xml file. So if you want a different name or package, than what I chose, remember to update that reference.

Now you should create and compile the HellowWorldServlet class using your Java development environment.

My sample servlet class looks as follows:

package dk.hosbruun.mvservlet.hello;
/* Copyright 2009 Peter Michael Bruun / www.hosbruun.dk
 * You are free to copy and modify this code for any legal purposes, whether private or commercial.
 * The code is provided "as-is" and comes with no explicit or implied warranty,
 * liability or fitness-of-purpose whatsoever.
 * This licence pertains to the shown code only, and implies no additional rights.
 */

import java.io.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorldServlet extends HttpServlet
{
  private static final String APP_PATH = "/mvservlet";
  private static final String DBNAME = "dbname";
  private static final String JDOCS = "jdocs";
  private static SimpleDateFormat DF = new SimpleDateFormat("HH:mm dd-MM-yyyy");
  
  private static String dbname = null;
  private static String jdocs = null;
  private static String here_dir = null;
  
  public HelloWorldServlet() {
  }

  public void init (ServletConfig sc) throws ServletException
  {
    super.init (sc);
    
    here_dir = sc.getServletContext().getRealPath("/");
    dbname = getInitParameter (DBNAME, "mydb");
    jdocs = getInitParameter (JDOCS, "/share/1000/Documents/web/jdocs");
  }

  private String getInitParameter(String parm, String dflt) {
    String res = getInitParameter(parm);
    if (res != null) {
      return res.trim();
    } else {
      return dflt;
    }
  }

  public void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException
  {
    try {
      ServletOutputStream os = response.getOutputStream();
      final String dbVersion = getPostgresConnection("HPMediaVault", dbname, "pmb", "ABC").getMetaData().getDatabaseProductVersion();
      os.println(
        "<html>\n"
        + "<head><title>Hello World</title></head>\n"
        + "<body>\n"
        + "<h1>Hello from Media Vault Java</h1>\n"
        + "Result of getContextPath: "+request.getContextPath()+"<br>\n"
        + "Result of getRequestURL: "+escape(request.getRequestURL())+"<br>\n"
        + "Result of getServletPath: "+escape(request.getServletPath())+"<br>\n"
        + "Result of date: "+today()+"<br>\n"
        + "Result of getQueryString: "+escape(request.getQueryString())+"<br>\n"
         + "Result of getSession.id: "+request.getSession().getId()+"<br><br>\n"
        + "Here dir: "+here_dir+"<br>\n"
        + "JDOCS dir: "+jdocs+"<br>\n"
        + "DB name: "+dbname+"<br>\n"
        + "DB version: "+dbVersion+"<br><br>\n"
        + img("Our cat", "kat.jpg", "The Cat")
        + "</body>\n"
        + "</html>"
      );
   } catch (Exception e) {
      throw new ServletException(e);
    }  
  }

  public static String img(String title, String imagefile, String alt) {
    return "<img title=\""+title+"\" src=\""+APP_PATH+"/images/"+imagefile+"\" alt=\""+alt+"\">";
  }

  public String getServletInfo() {
    return "Sample Java Servlet - Copyright 2009 Peter Michael Bruun";
  }

  public static synchronized String today() {
    return DF.format(new Date());
  }
  
  /* Warning! As the environment is multi-threaded, the connection will be
   * shared by all threads, which may not be a terribly good idea.
   */
  private static Connection CONN = null;
 
  public static synchronized Connection getPostgresConnection(String host, String dbname, String user, String pwd) {
    if (CONN != null) return CONN;
    try {
      Class.forName("org.postgresql.Driver");
      CONN = DriverManager.getConnection(
          "jdbc:postgresql://" + host + "/" + dbname, user, pwd);
      return CONN;
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
      throw new RuntimeException("Cannot locate PostgreSQL Driver: "
          + e.getMessage());
    } catch (SQLException e) {
      e.printStackTrace();
      throw new RuntimeException("SQL Error: " + e.getMessage());
    }
  }
  
  public static String escape(String s) {
    if (s == null) return s;
    return s.replaceAll("&", "&amp;").replaceAll("<", "&lt;");
  }
  
  public static String escape(StringBuffer s) {
    if (s == null) return "null";
    return escape(s.toString());
  }
}
The servlet assumes that I have created an image "kat.jpg" in a directory called
  /usr/jetty-5.1.12/webapps/mvservlet/images
Feel free to modify this.

Deploying active code to the Internet - and even publishing the accompanying code - can be dangerous business. On an afterthought, I have inserted methods to escape the original URL contents - otherwise a strange URL could have had some interesting side-effects.

Note that I have provided some rudimentary code, showing how to connect to the PostgreSQL database on your MV using parameters passed from web.xml.

It will be a bad idea to use my sample code in case several Jetty threads are concurrently using your servlet, because they would be sharing the database connection.

Deploying the HelloWorld Servlet

Locate the HelloWorldServlet.class file generated by your Java compiler, and copy it to the MV. If you use my package name, it should be placed under:
  /usr/jetty-5.1.12/webapps/mvservlet/WEB-INF/classes/dk/hosbruun/mvservlet/hello/HelloWorldServlet.class
Alternatively, you can build a .jar file and place it under:
  /usr/jetty-5.1.12/webapps/mvservlet/WEB-INF/lib
Now, use the restart-jetty.sh script to re-start Jetty (remember to wait a while), and see if you can access your new application.

Compare with my page, which looks like this.

Using JSP

I have not tried this myself, but I can provide some general directions.

As there is no Java compiler on the MV, in order to use JSP, you need to pre-compile all pages into servlets using a JSP compiler to produce .java files for all your .jsp files.

Then you must compile the generated java classes and deploy the class files to the MV and register every one in the web.xml file.

To see how this would look, just inspect the web.xml file located in the WebShare application on the MV:

  /usr/jetty-5.1.12/webapps/Webshare/WEB-INF/web.xml
I suppose compilation of JSPs could be done using an instance of Jetty deployed on your development machine (presumably Windows). However, I found that Jetty is very eager to remove such generated files, so you need some magic incantations to prevent this.

Alternatively, you could locate and download and use a stand-alone JSP pre-processor. Possibly, also Eclipse may help you.

If you choose this path, I would strongly suggest that you use some automated mechanism for compiling and deploying the JSP pages and updating the web.xml file. Doing this manually with a large number of JSPs could easily become a nightmare.


Valid HTML 4.01 Transitional