Tuesday, November 20, 2007

Open file within a web-app - treat it as a resource

Opening a file within a web app is tricky, because you don't generally know the absolute path to the file and the relative path might not be immediately apparent.

Try loading it as a resource. A resource is some data (images, audio, text, etc) that can be accessed by class code in a way that is independent of the location of the code. The name of a resource is a '/'-separated path name that identifies the resource. -- Javadocs for ClassLoader in JDK 1.4.2.

In a web app, the path can be absolute, beginning from the WEB-INF/classes directory. If I have "myfile.txt" inside WEB-INF/classes, the following code should work ok.

BufferedReader reader = new BufferedReader(new InputStreamReader(
    getClass().getResourceAsStream("/myfile.txt")));

Monday, November 19, 2007

Print a stacktrace in your JSP

Update 4/03/2009 5:00:37 PM. Added notes about catching Throwable and not to leave this code in by mistake.

Sometimes I want to capture an error that destroys my JSP and display it right there. You can do this with JSTL and log4j as well, but this is the plain old vanilla JSP way.

<% try { %>
<p>You will see this.</p>
<% if (true) throw new IllegalStateException("Naughty!"); %>
<p>You will not see this.</p>
<%
} catch (Exception exception) {
out.println("<pre style=\"color: red\">");
exception.printStackTrace(new java.io.PrintWriter(out));
out.println("</pre>");
}
%>

The output is as per below.

You will see this.

java.lang.IllegalStateException: Naughty!
at org.apache.jsp.default_jsp._jspService(default_jsp.java:62)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:331)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:329)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:874)
at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
at java.lang.Thread.run(Thread.java:595)

Warning: this is just a measure for quick diagnosis and trouble shooting during development. Never leave this stuff in your code unless you have very good reason. A user should only ever see a friendly message like "Operation failed. Please try again or contact support". They should never see stack traces; they can't do anything with that information and it is confusing (and thus un-professional). This output should go to your logs instead.

I have worked on applications before where I needed to resort to this; something else in the infrastructure or framework can be swallowing the exceptions, or it is unreasonably hard to get the logs quickly (and easier to edit JSP!).

For those really nasty errors, catching java.lang.Exception isn't enough. For example, a java.lang.NoClassDefFoundError isn't an Exception and won't be caught by this. It's a java.lang.Error (Exceptions's only sibling). To catch any Exception or Error, change the catch statement to catch (Throwable exception).

Double Warning: be doubly sure never to leave a catch Throwable in your code. In all cases it means something drastic is wrong - something that your code absolutely cannot recover from.

Tuesday, November 13, 2007

Enums in Java 5

Some example uses of Java 5's enums. Shows that enums are types which can have data and behaviour. They are instantiated and can can be imported into other classes.

This class creates an enum with some instances.

package org.rbram.tests.enumtest;
public final class FruitEnum {
  public enum Fruit {
    // Instances of the enum as a comma separated list of constructor calls.
    Apple("red"), Banana("yellow"), Grape("green");

    // Enums can have instance variables.
    private String color;
    private int count;

    // Enums can have multiple constructors.
    Fruit(final String theColor) {
      this.color = theColor;
      this.count = 0;
    }

    // Enums can have behaviour
    public String getColor() {
      return this.color;
    }

    public int getCount() {
      return this.count;
    }

    public synchronized int addFruit(final int howMany) {
      this.count += howMany;
      return this.count;
    }

    public synchronized int takeFruit(final int howMany) {
      if (howMany > this.count) {
        this.count = 0;
      } else {
        this.count -= howMany;
      }
      return count;
    }
  }
}

This class imports and tests a few aspects of the enums.

package org.rbram.tests.enumtest;
import org.rbram.tests.enumtest.FruitEnum.Fruit;
public final class FruitTest {

  public static void main(final String[] args) {
    Fruit fruit = Fruit.valueOf("Apple");
    fruit.addFruit(23);
    System.out.println("I have " + fruit.getCount() + " " + fruit
        + "s, which are all " + fruit.getColor() + " in color.");

    fruit = Fruit.valueOf("Grape");
    fruit.takeFruit(12);
    System.out.println("I have " + fruit.getCount() + " " + fruit
        + "s, which are all " + fruit.getColor() + " in color.");

    try {
      // You have to get the id right or catch the runtime exception.
      fruit = Fruit.valueOf("Durian");
    } catch (IllegalArgumentException exception) {
      System.err.println("Could not find fruit named Durian.");
    }

  }
}

Output of running the above code.

I have 23 Apples, which are all red in color.
I have 0 Grapes, which are all green in color.
Could not find fruit named Durian.

Other Resources

Monday, November 12, 2007

How to make a Jar File

Update. 15/12/2008 12:12:00 AM. Added trouble shooting section based on my first comment. Updated formatting.

Update. 18/12/2008 10:54:10 AM. Added to the trouble shooting section. Removed the Warning in the section "Run Executable JAR from Explorer (Windows)" - it was wrong!

Jar files provide a way to group (and compress) multiple Java files into one "archive" file. If you write an API made up of hundreds of Java files, it is much easier to distribute and import the API using one jar file than manage directories of Java files. Jar archives can be opened with any archive application like WinZip or WinRar (both for Windows). Rar and zip commands are usually native to Linux distro's without any downloads.

Jar files can also be executed, meaning that a complete Java application can be distributed as a single jar and run from the command line or by double clicking the file in your window manager. This tutorial shows you how.

Index


Continuation characters

In certain code snippets below, I use the continuation character "\". This enables a single command to be spread across multiple lines.

Bash continuation character (I use Bash/Cygwin/Windows). It means I can copy and paste the four lines below into a Bash terminal and have them executed as one command.

jar cf HelloWorld.jar \
  org/bram/helloworld/HelloWorld.class \
  org/bram/helloworld/SecondHelloWorld.class \
  org/bram/helloworld/ThirdHelloWorld.class

This shows the same code using the Windows-DOS continuation character. It means I can copy and paste the four lines below into a DOS command prompt and have them executed as one command.

jar cf HelloWorld.jar ^
  org/bram/helloworld/HelloWorld.class ^
  org/bram/helloworld/SecondHelloWorld.class ^
  org/bram/helloworld/ThirdHelloWorld.class

INDEX


Java Files

Gather your Java source. Here is a simple Hello World.

package org.bram.helloworld;
import javax.swing.JOptionPane;
public class HelloWorld {
  public static void main (String [] args) {
    JOptionPane.showMessageDialog(null, "Hello World!");
  }
}

A quick command line test shows it can be compiled and run ok.

javac org/bram/helloworld/HelloWorld.java
java org.bram.helloworld.HelloWorld

I have made a few copies of this file, so I have a few Java files now.

bash3.2 User rbram on host itvw1846 in dir /cygdrive/c/Temp/rbram
Mon Nov 12 09:13 AM> javac org/bram/helloworld/*.java

bash3.2 User rbram on host itvw1846 in dir /cygdrive/c/Temp/rbram
Mon Nov 12 09:13 AM> ls org/bram/helloworld/
.   HelloWorld.class  SecondHelloWorld.class  ThirdHelloWorld.class
..  HelloWorld.java   SecondHelloWorld.java   ThirdHelloWorld.java

INDEX


How To Use Jar Command

Now that we have the Java source and class files, we should look at the 'jar' command. It is a utility program that can be run from the command line (DOS prompt or bash terminal for example, depending on your OS). Here is how to create a compressed JAR file:

jar cf archive_name.jar files

Let's look at each part of that command line.

  • jar - the command to run the jar utility.
  • cf - "c" indicates that we wish to create a jar file (as opposed to extracting one, for example) and "f" indicates that we are going to specify a file name, i.e. "archive_name.jar".
    • Here is a list of the common options. I triggered the output below by issuing an invalid command to view the usage message.
    • bash3.2 User rbram on host itvw1846 in dir /cygdrive/c/Temp/rbram
        Mon Nov 12 09:02 AM> jar invalid
        Usage: jar {ctxu}[vfm0Mi] [jar-file] [manifest-file] [-C dir] files ...
        Options:
            -c  create new archive
            -t  list table of contents for archive
            -x  extract named (or all) files from archive
            -u  update existing archive
            -v  generate verbose output on standard output
            -f  specify archive file name
            -m  include manifest information from specified manifest file
            -0  store only; use no ZIP compression
            -M  do not create a manifest file for the entries
            -i  generate index information for the specified jar files
            -C  change to the specified directory and include the following file
        If any file is a directory then it is processed recursively.
        The manifest file name and the archive file name needs to be specified
        in the same order the 'm' and 'f' flags are specified.
      
        Example 1: to archive two class files into an archive called classes.jar:
               jar cvf classes.jar Foo.class Bar.class
        Example 2: use an existing manifest file 'mymanifest' and archive all the
                   files in the foo/ directory into 'classes.jar':
               jar cvfm classes.jar mymanifest -C foo/ .
      
  • archive_name.jar - name of the JAR file. This can only be included if you use the 'f' option.
  • files - names of all the files you want to put in the jar file. This could be just one name or a list of multiple names separated by space. Names can use pattern matching characters to match multiple files.

INDEX


Non Executable Examples of the Jar Command

So, I have a Java application consisting of three source files that I wish to distribute:

org/bram/helloworld/HelloWorld.java
org/bram/helloworld/SecondHelloWorld.java
org/bram/helloworld/ThirdHelloWorld.java

I also want to call my JAR file HelloWorld.jar. To make a JAR file with just HelloWorld.java:

jar cf HelloWorld.jar org/bram/helloworld/HelloWorld.java org/bram/helloworld/HelloWorld.class

This creates the following Jar

We can't run this file - it is not executable. All we have done is compress multiple Java files (a source and a class file) into a single artefact that you can distribute and include in other Java applications. For example, if someone wanted the magnificent HelloWorld functionality, they can include HelloWorld.jar in their project's classpath and use it in their own Java files.

import org.bram.helloworld.HelloWorld;

...

// Magnificent HelloWorld popup.
HelloWorld.main();

Here is how you can make a jar with all three files listed separately.

jar cf HelloWorld.jar \
  org/bram/helloworld/HelloWorld.class \
  org/bram/helloworld/SecondHelloWorld.class \
  org/bram/helloworld/ThirdHelloWorld.class

We could do the same thing using a pattern match.

jar cf HelloWorld.jar \
org/bram/helloworld/*.class

Of course, we want to include the source files too.

jar cf HelloWorld.jar \
org/bram/helloworld/*.*

It creates the following jar file.

Note that in this example, the source files are in the same directory as the class files are. This is a 'convenience' so that I have all the files in one place. It is common for publicly released API's to have two jars: one with the class files and a different jar with source files.

INDEX


Create An Executable JAR

In the screen shots above, you may have noticed the manifest files. The manifest file holds information about the jar that Java can use, such as the name of a Java class that should be run when if the jar file is executed. See Other Resources for some more detail about manifest files.

Create a text file that lists the "main" class, which references a Java class with a main method that will be run when the jar is executed. I create a text file called "manifest.txt" with the following contents.

Main-Class: org.bram.helloworld.HelloWorld

Note 1. This file MUST end with a blank line i.e. the text file I created had two lines - the line above plus a blank line.

Note 2. You must specify the fully qualified name of the class i.e. package + Java class name. You do not use the class file extension (.class) or source file extension (.java).

I can run the jar utility with a new option "m" and the name of the manifest file. This is the command I use.

jar cmf manifest.txt \
HelloWorld.jar \
org/bram/helloworld/*.java \
org/bram/helloworld/*.class

This is the result.

INDEX


Create An Executable JAR with Ant

Making jar files with a command line is easy for small projects. What happens when you need to jar an API that does have hundreds of files in different packages? The command line would start to get very large and unwieldly. You could put it into a .bat file (DOS) or .sh (*nix), but you still have the problem with the list of files getting large and hard to maintain.

A much better solution is to use an Ant build file. The advantage is that it lets you specify the contents in terms of directories, so it is much easier to maintain and repeat each time a new build is required.

The following would be saved as build.xml to the same base directory as the "org" folder is stored in.

<?xml version="1.0"?>
<project name="HelloWorld" default="buildJar" basedir=".">
  <target name="buildJar">
    <delete file="${basedir}/HelloWorld.jar" failonerror="false" />
    <jar destfile="${basedir}/HelloWorld.jar" basedir="."
        excludes="**/*.bak manifest.txt build.xml">
      <manifest>
        <attribute name="Built-By" value="${user.name}"/>
        <attribute name="Main-Class" value="org.bram.helloworld.HelloWorld"/>
      </manifest>
    </jar>
  </target>
</project>

To run this, simply run the command "ant" from the same base directory.

INDEX


Run Executable JAR from Command Line

I can run it from the command line like this.

java -jar HelloWorld.jar

Here is the result.

INDEX


Run Executable JAR from Explorer (Windows)

Tested on Windows XP If you find this is different on your version of Windows, email me at robertmarkbram AT gmail dot com and let me know.

Within Windows Explorer, you can double click on a jar file and have it automatically execute the main class. This makes jar files a suitable alternative to exe files - very handy for GUI applications distributed as jar files.

There are two caveats. Firstly, you need to have a JRE (or JDK) installed on the machine that will run the jar. Secondly, an association must be set up that tells Windows that .jar files should be run be run by "javaw.exe", which is part of any JRE or JDK installation.

Open Windows Explorer and select "Tools | Folder Options | File Types". You might see that jars are set up by default when you installed Java, as the below screen shot shows. Click on "Advanced" to check it out further.

Clicking "Advanced" shows you the "Edit File Type" dialog below. Click "Edit" on the "open" action.

This is the dialog showing the "open" action. Note the "Application used to perform action".

For me, that value is as below.

"c:\IBM\SDP70\jdk\bin\javaw.exe" -jar "%1" %*

If you don't see the mapping, click "New" in the "Folder Options" dialog. Type "jar" as the "File Extension" and press "OK". Then you can click "Advanced" on the jar association and set up the "open" action as per the screen shots above.

Double clicking on the file executes it.

INDEX


Quick Trouble Shooting Guide

  • You can open jar files with winzip to see what is inside them.
  • You can have .java and .class files in a jar.
  • You must have a .class file with a main method that is referenced in the manifest in order to create an executable jar.
  • Look for error messages - i.e. an error dialog. If you see a command prompt pop up and disappear too quickly for you to see what was in it, try running the jar from the commandline using "javaw.exe -jar" - then you will see the error message in the command window
  • Failed to load Main-Class manifest attribute. Your manifest file doesn't specify the Main-Class attribute. See the create an executable jar section.
  • java.io.IOException: "invalid header field". Did you write your manifest file in Word or WordPad? This might be caused by illegal characters being in this file: try creating and saving the file in a text editor. Found this suggested in a sun forum post.
  • I run the Jar from the command line and see the output ok, but when I double click on my jar file I see no output - no errors either, but still, no output! I am outputting text using System.out.println("") statements. The reason you can't see anything when you double click on the jar file is because System.out.println("") outputs to a console: you have no console, since you didn't launch the application from one, and a console isn't automatically created for you when you launch a file with a double click.
    If you want to see output by double clicking, you can try a JOptionPane - an easy to use GUI component. Try this: javax.swing.JOptionPane.showMessageDialog(null, "My Message");.
  • Could not find the main class. Program will exit. There are a few possibilities.
    • Check the class name listed in your manifest file.
      • Is the class spelt correctly?
      • Is the package correct?
      • You can use slashes instead of periods e.g. my/test/HelloWorld or my.test.HelloWorld will work, so this isn't an issue.
      • Have you mistakenly ended the class name with .class or .java? If so, remove it.
    • Check your Jar File Types setting (for Windows).
      • Have you mistakenly left out some crucial spaces in the arguments list? Make sure you have spaces as per the red highlighted spaces in the following "C:\Program Files\Java\j2re1.4.2_05\bin\javaw.exe" -jar "%1" %*
  • XXX is not recognized as an internal or external command, operable program or batch file. In short, Java's bin dir isn't on your PATh. See my other blog post on this issue.
    • Are you using JDK 1.4 or lower with spaces in the path? E.g. C:\Program Files\Java\j2re1.4.2_05\bin\javaw.exe might need to be shortened using the DOS short form to this instead: "C:\Progra~1\Java\j2re1.4.2_05\bin\javaw.exe

INDEX


Other Resources

INDEX


DOM elements case sensitive in FireFox, not IE

I like a robust application, and case (in)sensitivity is often a good candidate, i.e. where possible, let your users type Answer, answer, ANSWER or ANswer. As long as you know the answer, does it matter what case they use?

Sometimes it can get in the way. A couple of times I have stumbled on this one. Here is some Ajax style Javascript I was using in a page today. It worked in IE, but not in FireFox.

function updatePage() {
  if (request.readystate != 4) {
    return;
  }
  if (request.status != 200) {
    alert("Error with request.status [" + request.status + "].");
  }
  var customerAddress = request.responsetext;
  document.getElementById("address").value = customerAddress;
}

It was my sloppy typing of course, but I found it to be a hard mistake to track down, and annoying because I thought the code was working fine until I opened it in FireFox.

When I used readystate instead of readyState in FireFox, the function always returned.

When I used responsetext instead of responseText in FireFox, I would always see "undefined" in my address field.

Ultimately, this is one instance where I do wish IE was case insensitive, so it would be a lot harder to write code that works in one place and not the other. (Of course, you might argue that FireFox should be case insensitive... I won't, but you might. I like my scripting to be a little more precise.)

HTML DOCTYPE is important in IE

I put the following in a HTML page and display it in IE 6.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<style type="text/css" >
BODY {
 color: darkred;
 font-family: sans-serif;
 margin: 2em;
 padding: 1em;
 border: double darkred;
}
</style>
</head>
<body>
<p>Text</p>
<p>Text</p>
</body>
</html>

Here is what it looks like.

Now, here is what it looks like if you leave out or screw up the DOCTYPE:

There is no big difference in display with FireFox - the border seems to be displayed the same either way. But in IE, the CSS for the border is only correctly displayed with the DOCTYPE in place. The other style rules are still obeyed i.e. the text is still sans-serif, either way.

Sunday, November 11, 2007

Ajax Callback function not getting called

In a HTML page I was using a global Javascript request object.

<script type="text/javascript">
var request = null;
try {
  request = new XMLHttpRequest();
} catch (trymicrosoft) {
  try {
    request = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (othermicrosoft) {
    try {
      request = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (failed) {
      request = null;
    }
  }
}
...
</script>

The first time I executed my Ajax Javascript to issue a request to the server, the callback function worked ok and callback function was called. Each subsequent request hit the server ok, but the callback function wasn't called again. I didn't see any Javascript errors either. Turned out I had my onreadystatechange and open calls in the wrong order. This is how they were:

request.onreadystatechange = updatePage;
request.open("GET", url, true);
request.send(null);

When I made the following change, it worked.

request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);

Another fix was when I had the code to make a request inside a single function and being called before the three request lines, like below. Obviously, this was just hiding the problem though.

<script type="text/javascript">
function createRequest() {
  try {
    request = new XMLHttpRequest();
  } catch (trymicrosoft) {
    try {
      request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (othermicrosoft) {
      try {
        request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (failed) {
        request = null;
      }
    }
  }

  if (request == null) {
    alert("Error creating request object!");
  }
}

function doAjaxyThing() {
  var url = "...";
  createRequest();
  request.onreadystatechange = updatePage;
  request.open("GET", url, true);
  request.send(null);
}
</script>

Tuesday, November 06, 2007

Firebug for debugging Javascrpt - turn it off in Production

The Firebug and Web Developer plugins for Firefox are simply awe-inspiring. Using these two, I was able to show my page as it should look when printed (it had separate print media css) - Web Developer - and then use Firebug to dynamically update the CSS until I was happy with it.

Firebug lets you examine DOM, HTML, Scripts and Net statistics. The Developer Toolbar lets you take apart and examine your page in various funky ways. There is a lot in common between Firebug and the Developer Toolbar.

On IE, MS Developer Toolbar gives you some of these capabilities too, and I use it a lot.

One of the most useful features of Firebug is the Console API. It gives a small collection of files you put into your web app, and then you can log messages from Javascript - so much more powerful that using alert() calls or the status bar.

You can print out logging messages:

console.log("message");
console.debug("message");
console.warn("message");
console.error("message");

And there are more complicated uses like:

console.dir(object);

This prints an interactive listing of all properties of the object. This looks identical to the view that you would see in the DOM tab (in Firebug).

Or how about:

console.dirxml(node)

This prints the XML source tree of an HTML or XML element. This looks identical to the view that you would see in the HTML tab. You can click on any node to inspect it in the HTML tab (in Firebug).

Press F12 to show the console. Once you see the console, you can also enter any of the above commands in the command line shown.

You would generally use Firebug for debugging during some development stage and would remove the various console statements before migrating forwards. It is easy to forget this though, and find you have let code go into Production with a few console statements left in. I use the following JSP to import Firebug. This uses firebug.js if I am doing local development - i.e. accessing the page using "localhost". It uses firebugx.js for other environments - i.e. when the environment is accessed through proper host name - which provides versions of all calls that do nothing.

<% if (request.getServerName().equals("localhost")) { %>
  <script type="text/javascript" src="<%=request.getContextPath()%>/include/firebug/firebug.js"></script>
<% } else { %>
  <script type="text/javascript" src="<%=request.getContextPath()%>/include/firebug/firebugx.js"></script>
<% } %>

IE 6 caching my Ajax RPCs

I am writing some Ajax (GET) calls and Firefox is fine - each call returns the expected result. But under IE 6 it is returning cached results.

Turn off cache

I can turn caching off and this fixes it. Tools | Internet Options | General tab | Temporary Internet Files: click Settings | "Check for newer versions of stored pages": click "Every visit to the page".

But that's not nice - I don't want to turn caching off for all of my pages, and I don't want to ask users to turn their cache off either.

Use POST!

Yet if you read the HTTP specification, you'll learn that any resource got using GET method is due to be cached by any agent on the chain (browser, proxy, even web server); whereas if got using the POST method, the query will be resubmitted and reprocessed each and every time. - Ajax IE Caching Issue

Make your URLs unique

Make Your Remote Procedure Calls Have an Unprecedented URL  - from HowToAdvice.com.

If you have to use GET, add a new parameter to your URL to make it unique each time:

var url = "http://www.yourdomain.com/?id=47" + "&ms=" + new Date().getTime();

Personally, I changed to POST.

BTW - I found this problem while going through the examples in Head Rush Ajax, a very cool way to intro to this subject. After making this post, I flipped the page to page 59 and found they knew about this issue and deal with in Chapter 2. The say to add the dummy new Date().getTime() parameter. :)