It’s Officially Winter..

We got a relatively large dump of snow this past Sunday night and then experienced one of those bitterly cold and blindingly bright winter mornings that make the hour or two or shoveling all worth it. I snapped a couple of pictures (the one below is of the sun setting from our back porch) which you can see here.

snow_in_mattapoisett.jpg

Automated Application Deployment with Tomcat: Part II with Subversion And Ant

Got around to writing the deployment scripts that go with the Subversion work I did last week. As part of the scripts, I also worked on the problem I wrote about a couple weeks ago (ie: needing different log4j properties files per environment). Once you have a source control solution and a master build file, it’s pretty easy. I stripped out the explicit log4j category and appender directives and instead used the Ant <propertyfile> tag to write out the appropriate category / appender directives based on the presence (or lack thereof) of the project.debug property in my build file. When I want to create a build that outputs logging information to the console, this block of code is fired:
<target name="build.resources.debug" if="project.debug">
  <propertyfile
    file="WEB-INF/classes/log4j.properties">
    <entry key="log4j.rootCategory" value="INFO, STDOUT"/>
  </propertyfile>
</target>

Otherwise, this block is run:
<target name="build.resources.production" unless="project.debug">
  <propertyfile
    file="WEB-INF/classes/log4j.properties">
    <entry key="log4j.rootCategory" value="ERROR, SMTPAPPENDER"/>
    <entry key="log4j.category.com.mycompany" value="ERROR, SMTPAPPENDER"/>
  </propertyfile>
</target>

In Eclipse, you can easily create the project.debug property using the Arguments text box provided in Run –> External Tools –> $your ant build –> Main Tab –> Arguments: -Dproject.debug=true. By default then, log4j will only log error conditions and will send them to the SMTP appender.

The second thing I did was to create a deployment Ant script that wrapped my main build.xml. It checks out the source code from Subversion:
<exec executable="svn">
  <arg line="co svn://sourcecodeserver/myproject/branches/v1.0 . --username username --password password"/>
</exec>
, runs the normal build:
<ant antfile="build.xml" />
, stops the Tomcat service:
<exec executable="net">
  <arg line='stop "Apache Tomcat"'/>
</exec>
, deletes the existing web application from the Tomcat webapps directory:
<delete dir="${tomcat.install}myapp"/>
, creates and unpacks the generated war to the Tomcat webapps directory:
<mkdir dir="${tomcat.install}myapp"/>
<unzip src="myapp.war" dest="${tomcat.install}myapp"/>
and then starts the Tomcat service:
<exec executable="net">
  <arg line='start "Apache Tomcat"'/>
</exec>

We’ll see how well it works once this goes into production. If nothing else, at least I’ll have a couple more hours in my day.

Source Control with Subversion on Windows

We’re getting around to hiring an additional me at work so I needed to get a source control solution in place before he/she arrives and starts hacking away at the codebases. At the last place I worked we used Visual SourceSafe for a long time (which was pretty worthless) and then started using CVS, although not on the projects I worked on. A couple weeks ago I asked a bunch of questions about automating Java web application deployment and Erki suggested that I buy and read the Pragmatic Project Automation book, which I did and which I loved. I read it from start to finish (which isn’t an easy thing to do with a technical book) and took a ton of notes. Anyway, I’m pretty sure the book didn’t mention Subversion even once, but it did drill home the importance of setting up your code in source control and then doing your manual, scheduled, automated (using CruiseControl) or production builds directly from the source control system using Ant or Groovy, which led to alot of ideas about how you could automate builds to production Tomcat systems. I’ve heard alot about Subversion; it’s described as CVS with none of the warts and all of the features, so I tried it out. Following are the notes I kept during the installation process. Hopefully this helps out someone somewhere down the road:

a) Download Subversion (obviously) I downloaded version 1.1.1 ( svn-1.1.1-setup-2.exe)

b) Run the install. I chose to install to c:\subversion\.

c) Test the Subversion install by going to the install directory and then going to the /bin directory. Run the svnadmin from the command line:
CD c:\subversion\bin\
svnadmin

If you get an error that says that says “svnadmin.exe – Unable To Locate DLL”, you’ll need to either a) install the Visual C++ 6.0 VCREDIST.exe, which is available here (but only if you’re using Windows 98 or Windows ME) or b) find someone or some system that has msvcp60.dll and copy that file to c:\$windows\system32\ (where $windows is the directory that contains your Windows installation).

d) Create a repository on the machine that is going to host your source code. From the command line on the server machine:
svnadmin create d:\$projects\$projectname

where $projects is the name of a folder that will contain all your Subversion source code repositories and $projectname is the name of the specific project you’re going to create a repository for.

e) Start the server either from the command line:
svnserve -d -r d:\$projects

or use the SVN Service Wrapper (http://dark.clansoft.dk/~mbn/svnservice/), note that the r switch limits directory browsing by specifying the root of your repositories.

f) Create a users database (which is nothing more than a text file, I created a file called users.conf) and save it in the d:\$projects\$projectname\conf directory and then add your usernames & passwords. An example user database might look like this:[users]
aaron = mypassword

g) Within the d:\$projects\$projectname\directory, open the conf/svnserve.conf file using your text editor of choice and add the following to allow reading and writing to the users in the database we just created:
[general]
anon-access = none
auth-access = write
password-db = users.conf
realm = $projectname

where $projectname is the name of the project you’re setting up and users.conf is the name of the file you created in step f.

h) Finally, restart the server and you’re ready to start using the client and import your project.

i) To import an existing project, invoke the svn executable from the command line on the client:
svn import c:\temp\myproject svn://myserver/myproject -m "initial import" aaron mypassword

where c:\temp\myproject is the directory that contains the existing project you want to import (and should have 3 folders: trunk, tags and branches, your source code should be in the trunk folder; more on repository layout here), myserver is the name of the server that contains the Subversion repository you created in step d, myproject is the name of the project (ie: $projectname from step d) you’re importing into, the m switch signifies the message you want to tag this import with, followed by the username and password you setup in the users database in step f.

j) Finally, checkout the project from the server (invoking the svn executable from the directory that you want the trunk checked out from…, ie: c:\projects\myproject)
svn co svn://myserver/myproject/trunk . aaron mypassword

k) To add, move, delete, or edit and then commit any changes back to subversion:
Add: svn add src/myfile.java -m "adding a file"
Move: svn move src/myfile.java src/mynewfile.java -m "moved myfile.java to mynewfile.java"
Delete: svn delete src/myfile.java -m "removing a file"
Commit: svn commit src/myfile.java -m "the message"

That’s it… there’s a alot more to read. One of the coolest things about Subversion is that the documentation is all packaged up into a free PDF published by Oreilly, which you can download here. I printed out the manual and breezed through it in a couple hours. You should too.

I did try to get the Subclipse Eclipse Subversion plugin working, but for the life of me… I couldn’t. I think it ended up for the better anyhow because while the command line is a bit more tedious, I understand more about what’s going on in the system, which is helpful. I’d recommend that you get familiar with the command line client before attempting to use a GUI plugin.

After you’re all set with Subversion and you’re ready to get started with CruiseControl, note that while Cruisecontrol contains an <svn> tag, you still need to drop down into command line mode in Ant to checkout / update your local repository when doing an automated build. For example:
<exec executable="svn">
  <arg line="update"/>
</exec>

Java static initializer question

I ran into a problem with a section of code that lives within a Java static initializer; I’m not sure that the problem is the static init. Let me step back and frame the problem first. I have a Struts action class that presents a form for collecting date information; said form includes a drop-down menu that is bound to an ArrayList of LabelValueBean objects that contains the months of the year (Jan|1,Feb|2,Mar|3, etc..). The ArrayList of LabelValueBean objects is retrieved from a data provider class using a static method:
Collection months = MyDateProvider.getMonths();
Here’s the pseudo-code inside the date provider class:
public class MyDateProvider {
  private static Collection months = new ArrayList();
  
  static {
    Calendar c = Calendar.getInstance();
    SimpleDateFormat formatter = new SimpleDateFormat("MMM");
    SimpleDateFormat monthformatter = new SimpleDateFormat("MM");
    for (int i=0; i
The problem is that somehow the ArrayList of LabelValueBean objects contained two instances of March and no February, so the dropdown list would look like this:
January
March
March
April
May
June
July
August
September
October
November
December

Restarting Tomcat temporarily fixed the problem (obviously because MyDateProvider was then reloaded), but my question is how did is it possible for the ArrayList to be missing February and contain two instances of March? Any takers?

Update: the problem has nothing to do with the static init and everything to do with the way I used the Calendar class. The Calendar.getInstance() method returns a Calendar object initialized with the current date and time, so when the web application was restarted on 11/30/2004, the Calendar object reflected that date/time. Looping from 0 to 11 and setting the month (c.set(java.util.Calendar.MONTH, i);) resulted in the month changing correctly EXCEPT during the month of February , which has only 29 days. The date "02/30/2004" is illegal and so the Calendar class helpfully bumps the month up, which resulted in two instances of March in my drop down. Thanks to Paul Hastings for pointing this out.