Archive for June, 2006

Ruby threads and processes

I made quite a bit of progress in reading the Pick Axe Book over the weekend. I wrote a little program to reflect what I read and to scratch an itch of mine too.

I have three processes that I want to run at all times if it can be helped, but it's ok if there is some downtime for each. Whether these processes are started at system boot time or any time later doesn't make a difference to me, if they go down I want to start them back up. For a (very short) time I just blindly used the cron for my user account to start the processes once every X hours. Unfortunately this meant that five instances of the Unreal Tournament 2004 server would be running at once. svnserve would fail and send me email every few hours when it was invoked and JBoss would consume far too many resources because it was running too many instances. Using the cron to blindly kick off the servers was a bad idea.

Because I wanted to use the cron or an equivalent to periodically check on the servers I had to come up with a way to determine if the servers were already running before trying to start them again. I came up with two short Ruby scripts to solve this problem. The first is a class to represent the process to monitor and start.

class ProcessMonitor

  def initialize(name, processString, restartCmd)
    @name, @processString, @restartCmd = name, processString, restartCmd
  end

  def get_process_list
    @ps = IO.popen("ps -ef", "r")
  end

  def is_running?
    re = Regexp.new(".*?#{@processString}.*?")
    @ps.each do | line |
      if re.match(line)
        return true
      end
    end
    return false
  end

  def restart_process
    puts "Process  is not running.  Trying to restart."
    process = Process.fork { system("#{@restartCmd}") }
    Process.detach(process)
  end

  def run
    get_process_list
    if !is_running?
      restart_process
    end
  end

end

The constructor takes a name (which doesn't do anything at this point), a substring of the process to look for in the ps output and the command used to restart the process if it isn't running. The get_process_list serves only to get a ps listing of all the processes running.

The is_running? method examines each entry in the listing until it finds a process that matches the processString snippet passed in when the object was created. The run method will execute the restartCmd string only if the process is not running. The restart_process method uses the Process.fork method to start the command as a process outside the current Ruby interpreter. It then detaches any interest in that long running process, enabling the Ruby interpreter to exit normally.

I should point out that I had some trouble debugging what turned out to not be a problem. I developed this class in Eclipse using and the Ruby interpreter would not terminate when run from inside Eclipse. Running the program on the command line works without this strange hang.

Because I do not want to provide a cron entry to call this class for each process I want to monitor I have written a small driver class that takes input from a file.

require "process_monitor"

filename = ARGV[0]

processes = Array.new

IO.foreach(filename) do | line |
params = Array.new
line.each("::") do | param |
params

This script takes one filename as its argument. Each line in the file should contain a "::"-delimited list of parameters to create a ProcessMonitor object with. After parsing each line a new ProcessMonitor object is created and run in a new thread. After all threads have been run the script waits for each to finish and the program exits. I plan to put error handling and write test cases sometime this week.

Constructors and Garbage Collection

I'd like to post some Java code illustrating how static initialization blocks, constructors and the finalize() method are used. These examples should be common knowledge for any experienced Java developer. First here are the two classes I've created to demonstrate:

Class: Guitar

package com.anthonychaves.scratch;

public class Guitar {
	int numStrings = 6;

	static final String nameBrand = "Fender";

	static {
		System.out.println("This is the com.anthonychaves.scratch.Guitar static initializer.");

	}

	public Guitar() {
		System.out.println("This is the com.anthonychaves.scratch.Guitar constructor.");
	}

	public void finalize() {
		System.out.println("This is the com.anthonychaves.scratch.Guitar#finalize() method.");
	}
}

Class: TwelveStringGuitar

package com.anthonychaves.scratch;

public class TwelveStringGuitar extends Guitar {

	public TwelveStringGuitar() {
		numStrings = 12;

		//nameBrand = "Celebrity"; 						// I can't do this because nameBrand is final
		nameBrand.replaceAll("Fender", "Celebrity");	// so I have to do this instead.

		System.out.println("This is the com.anthonychaves.scratch.TwelveStringGuitar constructor.");
	}

	public static void main(String args[]) throws Exception {
		Guitar anthonysGuitar;
		System.out.println("Static initializer should have been called.");
		Guitar mikesGuitar = new TwelveStringGuitar();
		anthonysGuitar = new TwelveStringGuitar();
		mikesGuitar = new Guitar();

		Thread.sleep(10000);
		System.gc();
		Thread.sleep(10000);
		System.out.println("End of main.");
	}
}

The main entry point into this code is in the

TwelveStringGuitar#main(String)

method. The first thing it does is declare anthonysGuitar of type Guitar. When this happens the Guitar class is loaded into the JVM and the static initializer block is called at load time, not when an object of type Guitar is instantiated. Because of this restriction only static class variables are accessable within a static initialization block. An object of type Guitar does not exist yet and therefore the numStrings variable can not be used in that static block.

The second thing to point out is that an object always calls the constructor of its parent class, explicitly or not, preceeding any other lines of code. The

TwelveStringGuitar

constructor does not have a line that says

super()

but the Guitar() constructor is called anyway. If you were to compile and run these classes the line This is the com.anthonychaves.scratch.Guitar constructor will always be called before This is the com.anthonychaves.scratch.TwelveStringGuitar constructor.

The last thing to point out in this code is the use of the

finalize()

method. This method is inherited from Object and is called by the JVM when the garbage collector is about to destroy the instance of the class. In the example mikesGuitar is shown to first be a TwelveStringGuitar and later a regular Guitar. After the line

mikesGuitar = new Guitar();

the previous TwelveStringGuitar object mikesGuitar referred to is sitting in the heap with zero references to it. Because of this, and requesting the garbage collector be run by calling System.gc(), that TwelveStringGuitar object can be destroyed. Before it is destroyed the JVM calls finalize(), which TwelveStringGuitar inherits from Guitar, and the notice This is the com.anthonychaves.scratch.Guitar#finalize() method is printed to the console.

I chose to use the keyword final and the finalize() method in this example to show how they are in no way related to one another. The keyword finally is also not related to either final or finalize(), only used as a “no matter what do this step last” clause in a try block.

That's all for this quick demonstration. As always comments are welcome.

render, redirect_to and instance variables

I rediscovered something about Rails instance variables tonight after initially discovering it a few weeks ago and not writing it down. I spent a good 30 minutes trying to figure out why the following code segment would not produce my expected results in my browser.

  def save
    @bookmark_file = BookmarkFile.new(params[:bookmark_file])
    @bookmark_file.user_id = session[:user].id
    parse_file @bookmark_file.data
    @bookmark_file.parsed_on = Time.now
    @bookmark_file.save
    redirect_to :action => 'list_parsed_bookmarks'
  end

On the browser side I had:


      -- unimportant lines omitted --
      -- just display the bookmarks in the collection --

I had an instance variable @bookmark_file with a one_to_many relationship with bookmarks that I wanted to display on the page for a user. Unfortunately when I tried to display the page in Firefox I got the following error:

You have a nil object when you didn't expect it!
The error occured while evaluating nil.bookmarks

My @bookmark_file instance variable was nil in the view when it was clearly set in the controller's save method. On the surface it would seem that the @bookmark_file instance variable became nil somewhere between the controller and the view. Examining the redirect_to method yields a slightly different answer though.

According to the Rails documentation, ActionController::Base#redirect_to redirects the browser as its name implies. As far as the Rails routing engine is concerned this is a completely new request and all instance variables are thrown out. SO it's not that my @bookmark_file variable was set to nil on the way to the view, rather it wasn't even my @bookmark_file. The new request didn't set any @bookmark_file so there was nothing for the view to operate on.

ActionController::Base#render actually displays the HTML produced by the action or view specified when it is called. By replacing the call to redirect_to with a call to render in the controller the application works as expected because Rails will find the list_parsed_bookmarks view for the controller and use the current instance variables when rendering the view. WIth @bookmark_file already set in the save method the view is then free to use the initalized object.

It may be possible to get the desired behavior out of the original way the method was written by storing @bookmark_file in a session variable but I haven't tried and I don't think it would be a good idea to do so. Using session variables implies that data is shared across requests and creating another request to display the data rather than continue with the current request seems foolish in this situation. The potentially high volume of data stored across all sessions is also a good deterrent.

Any comments are welcome.