Debugging JSP pages on GoDaddy
I was recently hired to look into some problems with a fairly simple web-based software licensing application hosted on GoDaddy. The server application consists of a number of servlets in some JAR files, and some .jsp pages invoking those servlets.
One of the problems was that email messages originated by the server application weren’t being delivered. There were two issues affecting email delivery. Firstly, the SMTP server had been hard-coded as ‘localhost’; this was easily changed to GoDaddy’s mail server. After this change was made, the second problem appeared: the qmail server was complaining about raw linefeed characters (LF vs CRLF). The app uses an email servlet from CoolServlets.com (now apparently defunct), which uses PrintWriter.println
to issue each SMTP command. The email messages constructed by the app used \n
newline characters. Neither of these approaches is correct: PrintWriter.println
emits the system-specific newline, and no Java IO function will ever do C-style translation of \n
into \r\n
. A succinct little summary of newlines in programming languages can be had from Wikipedia.
The fix is to change every out.println(stuff)
in com.coolServlets.email.Transport.java
to out.print(stuff + "\r\n")
, and similarly to construct email messages using \r\n
instead of \n
. If you need to make a similar fix, the source for the CoolServlets.com email servlet is, at the time of writing, available here.
So far, so straightforward. The only issue here is that GoDaddy’s Tomcat server only restarts once a day (1am Arizona time). And because of servlet caching, updates to a jar (or standalone servlet) only come into effect when the server is bounced. So if you’re trying to debug a server application that depends intimately on the host’s (GoDaddy’s) email server & MySQL databases, the naive MO is basically patch, compile, upload, wait til 1am AZ time, repeat until done. Nightmare. Other people have encountered similar GoDaddy Gotchas.
However, while an update to an existing servlet has to wait for the bounce, a new servlet (standalone or in a .jar, but not in a WAR) is available immediately on upload. So for a faster turnaround time, you can rename the class each time you upload it, and invoke it straight away. Of course, this gets cumbersome pretty quickly, as every reference to the updated class in every .java and .jsp file has to be updated as well.
So the fix is: automation by shell script. For the application I was working on, I had two servlet source trees: /WEB-INF/lib/com.consultant/
(consultant
were the crowd that originally developed the application), and /WEB-INF/lib/com.coolservlets/
. I also had a bunch of .jsp files in /jsp/
. I figured the simplest thing was to update the package names, specifically changing com.
to comTestNNN
. Don’t use comN
, as com1
and com2
are serial ports under Cygwin.
The shell script (update.sh
) appears below. As it stands, you invoke it as update.sh com comTest1
after the first fix. If the problem’s not sorted, make another change and go update.sh comTest1 comTest2
. And so on, with a new basename for the updated packages every time. You can test the updated application immediately it’s uploaded.
The main flaw in the system is that you have to close all the source files in your editor before each build—even if the build fails—as the root directory name changes each time the script is invoked, so the file paths are invalidated. I suspect that this can easily be worked around using directory links, however, where the directory link is renamed each time while the code sits inside the ‘real’ directory. Also, it would be smarter to have the script generate the new name automatically, rather than have the user specify the old and new names.
The four stages are:
- Rename the package root directory from $OLDNAME to $NEWNAME
- Replace every occurence of $OLDNAME to $NEWNAME in all .java and .jsp files, if referring to one of the packages we’re updating
- Package up the files in a .jar
- Upload everything to the server
The script uses the Java tools (same version as GoDaddy, rather than the most recent), perl, and ncftp. It’s developed under cygwin, but should work on any bash-alike.
#!/bin/bash set -o nounset set -o errexit set -o verbose JAVAC=/cygdrive/c/Program Files/Java/jdk1.5.0_11/bin/javac.exe JAR=/cygdrive/c/Program Files/Java/jdk1.5.0_11/bin/jar.exe [ $# -lt 2 ] && echo "Need OLDNAME / NEWNAME arguments!" && exit -1 OLDNAME=$1 NEWNAME=$2 BASEDIR="e:/code/www.sampleserver.com" #can't use /cygdrive/e for win32 java tools FTPUSER=someguy FTPPASS=crazycrypticpassword JAVADIR=${BASEDIR}/WEB-INF/lib/${NEWNAME}/ JSPDIR=${BASEDIR}/jsp cd ${BASEDIR}/WEB-INF/lib mv $OLDNAME $NEWNAME perl -pi -w -e "s#${OLDNAME}.consultant#${NEWNAME}.consultant#g" `find $JAVADIR -name *.java` perl -pi -w -e "s#${OLDNAME}.consultant#${NEWNAME}.consultant#g" `find $JSPDIR -name *.jsp` perl -pi -w -e "s#${OLDNAME}.coolservlets#${NEWNAME}.coolservlets#g" `find $JAVADIR -name *.java` perl -pi -w -e "s#${OLDNAME}.coolservlets#${NEWNAME}.coolservlets#g" `find $JSPDIR -name *.jsp` "$JAVAC" -cp .;servlet.jar `find $JAVADIR -name *.java` #Can't use $JAVADIR here; puts full path in the jar "$JAR" -cf stuff.jar `find $NEWNAME -name *java -o -name *class` #-uvf: add files; verbose, see files as added ncftpput -u $FTPUSER -p $FTPPASS www.sampleserver.com WEB-INF/lib stuff.jar ncftpput -u $FTPUSER -p $FTPPASS www.sampleserver.com jsp `find $JSPDIR -name *.jsp`
For more on debugging servlets, check here.