Tuesday, January 22, 2008

Leopard for the Web Developer - Running Tomcat as a Service

In my experimentation with configuring multiple virtualhosts with SSL, I stumbled upon the magic of launchd within mac os x. I use it to make a call to ifconfig when I start up to create a new loopback interface, and it works splendidly. Knowing that it would work on startup, I thought I'd apply the principles to Tomcat.

The process of setting up Tomcat as a service in Leopard is extremely simple. It goes something like this:

  1. Download Tomcat
  2. Unpack Tomcat where you want it (/usr/local/tomcat is a good spot)
  3. Create a lauchd plist configuration to point to the tomcat executable
  4. Place that configuration file someplace where launchd will find it.
  5. invoke launchdctl to install the new service
Once you do steps 1 and 2, it's time to create the launchd plist file. Here's what mine looks like -- I'll explain what's going on following

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">






<string>Tomcat 6 Server</string>


OK, here's what's happening. The plist defines a dictionary data structure instructing launchd how it should work. You'll see a <key> followed by a value of some sort. I'll go key by key.

  1. Disabled - a boolean value.
    If you want to have this active, set the value to false, as it is here. If you want to disable the service without uninstalling it, just change the disabled value to true.
  2. EnvironmentVariables - a (sub)dictionary value.
    This is just a set of name value pairs defining environment variables that you might need. In our case, Tomcat needs to know what JAVA_HOME is. You can set more if you want by adding more <key> and <string> pairs
  3. Label - a String value.
    This one is really important. It needs to be a unique identifier for the launchd process. launchd keeps track of all the things it starts and stops with this Label value. Just make sure that it's unique. It's common practice to use the reverse-domain syntax that you see here (org.apache.tomcat.tomcat6). Incidentally, it's also common practice to name this plist file with the same label (org.apache.tomcat.tomcat6.plist)
  4. ProgramArguments - an array of strings.
    Think of this as what you would type at the command line. Basically, anything you can do at the command line can be placed in a ProgramArguments value. The first string in the array is the executable program that you wish to run. Anything following is an argument that you would pass to that command. In our case, this merely does the following command:
    /usr/local/tomcat6/bin/catalina.sh run
  5. RunAtLoad - boolean value.
    This is what makes this a service, telling OS X that this should run when starting up.
  6. ServiceDescription - String value.
    This helps in the console to figure out what's running. It should just be some sort of description identifying the process. In this case, Tomcat 6 Server.
And that's it for the configuration file. Go ahead and save it as org.apache.tomcat.tomcat6.plist on your desktop, or somewhere else obvious. We'll move it to where it needs to reside next.

Launchd looks in a couple of places for these kinds of configuration files. From the man pages of launchd:

~/Library/LaunchAgents         Per-user agents provided by the user.
/Library/LaunchAgents Per-user agents provided by the administrator.
/Library/LaunchDaemons System wide daemons provided by the administrator.
/System/Library/LaunchAgents Mac OS X Per-user agents.
/System/Library/LaunchDaemons Mac OS X System wide daemons.

Since I want tomcat to start up as a machine process, not a user one, I place it in /Library/LaunchDaemons. If I wanted to make it a user process, I put it in ~/Library/LaunchAgents. A couple notes about this. First, if you put your file in /Library/LaunchDaemons, the plist file needs to be owned by root. The easiest way to do this is to open Terminal and do a "sudo cp org.apache.tomcat.tomcat6.plist /Library/LaunchDaemons/". This will make root own the file so it can start up as a system process. Second, if you decide to place the file in ~/Library/LaunchAgents, you don't need root ownership, but you do need to make sure the directory exists. By default, Leopard does not have a "LaunchAgents" folder in the user Library. So, you might have to make that directory before you copy your file there.

Final step, you'll need the Terminal. It's install time.

If you placed the plist in /Library/LaunchDaemons, do this

sudo launchctl load /Library/LaunchDaemons/org.apache.tomcat.tomcat6.plist

If you placed the plist in ~/Library/LaunchAgents, do this

launchctl load ~/Library/LaunchAgents/org.apache.tomcat.tomcat6.plist

That should do it. You now have Tomcat installed as a service on Leopard.

Friday, January 18, 2008

JSON Recursion -- Making a Self Reference

I have been working on a simple JSON library in Java which will allow me to serialize a JSON string. I'm pretty satisfied with the results so far, and I intend to release the code, however, until today, there was one thing that threw me for a loop. Writing JSON including a self reference. Here's the problem

When you write the following JSON syntax:


You expect that when you access the 'me' property, it would give a reference to that object. However, what you get instead is a reference to the Window object. This happens because the closures that javascript has assigns the 'this' to the Window because during the construction of the new object, the new object doesn't yet exist, and 'this' still refers to the Window. Not what we wanted.

Here's the fix:
this.me = this;
return this;

Instead of assigning the 'me' attribute to 'this' right away, we construct the object, setting the 'me' attribute to a function. The 'me' function re-assigns its own value, and in effect, kind of self-destructs. Finally, we make the 'me' function return a reference to 'this'. 'me' is now assigned a reference to itself.

This method works within an eval as well. It also nests pretty well too. Here's a more complicated example, with a sub-object containing a self reference, as well as multiple self-references:
"me":function(){this.me = this; return this;},
"alsoMe":function(){this.alsoMe = this; return this;},
"subMe":function(){this.subMe = this; return this;}