Tuesday, November 20, 2007

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.

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.

Monday, November 12, 2007

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.

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.

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.

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.

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.

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.

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.

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.

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

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

My original tutorial on this subject. I made this when I was at Monash Uni. I don't control this page anymore; frankly it surprises me that this page is still up! Here is the home page for my work at Monash all that long time ago.

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.

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.)

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.

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:

Tuesday, November 06, 2007

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.

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.

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.

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

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. :)