Monday, March 09, 2009

Vertical Rush

I've been crazy enough to sign up for Vertical Rush, a race to the top of Tower 42, the tallest building in the City of London. The event, which is going to take place on the 19th of March, involves running or walking 183m (600ft) up 920 stairs and 42 floors to the top of the tower. All of this is in aid of the vulnerably housed and homeless charity Shelter.

Tower 42 is the tallest building in the City of London

I'm not doing this alone. I have a team of four others from work and we're calling ourselves, "Are we there yet?". We've been climbing 400 stairs (24 floors) in our office every day for the last couple of months, but this is going to be a lot more painful!

Please help to motivate us to the top by sponsoring us through our Just Giving page: http://www.justgiving.com/reteamstairclimb

Stay tuned to find out how long it takes me to climb the tower!

Tuesday, March 03, 2009

Howto: Delete Empty Directories [Unix]

Consider the following directory structure:
/tmp
 |-->bar.txt
 |-->dir1/
 |-->dir2/
 |    |-->baz.txt
 |-->dir3/
 |-->foo.txt
There are two files called bar.txt and foo.txt, a non-empty directory called dir2 and two empty directories called dir1 and dir3.

This is how you can delete only the empty directories:

sharfah@starship:/tmp> unalias rmdir
sharfah@starship:/tmp> rmdir *
rmdir: directory "bar.txt": Path component not a directory
rmdir: directory "dir2": Directory not empty
rmdir: directory "foo.txt": Path component not a directory
You need to unalias rmdir just in case you have it aliased to "rm -rf"! You will notice that rmdir does not delete files or non-empty directories. Only dir1 and dir3 are deleted.

Another way to do it, using find:

sharfah@starship:/tmp> find . -type d -exec rmdir {} \;
rmdir: directory ".": Can't remove current directory or ..
rmdir: directory "./dir2": Directory not empty
Note that aliases aren't recognised by find, so even if you did have rmdir aliased, it would not use it.

Friday, February 27, 2009

Get Yesterday's Date [Scripting]

Here is a useful snippet that you can include in your shell scripts if you need to find out what the previous day's date was.

Perl code:

$secsInADay=86400;
@yest=localtime(time-86400);
$year=$yest[5]+1900;
$month=$yest[4]+1;
$day=$yest[3];
print "$year$month$day";
One-liner for Shell Scripts
YEST=`perl -w -e '@yest=localtime(time-86400);printf "%d%.2d%.2d",$yest[5]+1900,$yest[4]+1,$yest[3];'`
echo $YEST
To get Tomorrow's Date
TOM=`perl -w -e '@tom=localtime(time+86400);printf "%d%.2d%.2d",$tom[5]+1900,$tom[4]+1,$tom[3];'`
echo $TOM

Wednesday, February 25, 2009

Quick Maven Commands

Creating a Maven project
In order to create a new maven project called MyProject run the following command:
mvn archetype:create -DgroupId=fs.work -DartifactId=MyProject
This will create a new directory called MyProject with a pom.xml and the following tree structure:
MyProject
 |-->pom.xml
 |-->src
 |  |-->main
 |  |  |-->java
 |  |  |  |-->fs
 |  |  |  |  |-->work
 |  |  |  |  |  |-->App.java
 |  |-->test
 |  |  |-->java
 |  |  |  |-->fs
 |  |  |  |  |-->work
 |  |  |  |  |  |-->AppTest.java
The pom file looks like this:

  4.0.0
  fs.work
  MyProject
  jar
  1.0-SNAPSHOT
  MyProject
  http://maven.apache.org
  
    
      junit
      junit
      3.8.1
      test
    
  

Creating sub-modules
If you need to create sub-modules within your project, you need to change the packaging in the pom file (i.e. the "super" pom), to pom. Then, from within the MyProject directory issue the following commands to create sub-modules:
mvn archetype:create -DgroupId=fs.work -DartifactId=MyProjectWeb -Dpackaging=war
mvn archetype:create -DgroupId=fs.work -DartifactId=MyProjectModule1 -Dpackaging=jar
This creates the sub-modules and the directory tree now looks like this:
MyProject
 |-->pom.xml
 |-->src
 |  |-->main
 |  |  |-->java
 |  |  |  |-->fs
 |  |  |  |  |-->work
 |  |  |  |  |  |-->App.java
 |  |-->test
 |  |  |-->java
 |  |  |  |-->fs
 |  |  |  |  |-->work
 |  |  |  |  |  |-->AppTest.java
 |-->MyProjectModule1
 |  |-->pom.xml
 |  |-->src
 |  |  |-->main
 |  |  |  |-->java
 |  |  |  |  |-->fs
 |  |  |  |  |  |-->work
 |  |  |  |  |  |  |-->App.java
 |  |  |-->test
 |  |  |  |-->java
 |  |  |  |  |-->fs
 |  |  |  |  |  |-->work
 |  |  |  |  |  |  |-->AppTest.java
 |-->MyProjectWeb
 |  |-->pom.xml
 |  |-->src
 |  |  |-->main
 |  |  |  |-->java
 |  |  |  |  |-->fs
 |  |  |  |  |  |-->work
 |  |  |  |  |  |  |-->App.java
 |  |  |-->test
 |  |  |  |-->java
 |  |  |  |  |-->fs
 |  |  |  |  |  |-->work
 |  |  |  |  |  |  |-->AppTest.java
The pom file for MyProjectModule1 contains a reference to the parent and looks like this:

  
    MyProject
    fs.work
    1.0-SNAPSHOT
  
  4.0.0
  fs.work
  MyProjectModule1
  MyProjectModule1
  1.0-SNAPSHOT
  http://maven.apache.org
  
    
      junit
      junit
      3.8.1
      test
    
  

Deploying a jar to the repository
If you have a jar file called myarchive.jar which you want to upload to your maven repository, use the following command:
mvn deploy:deploy-file -Durl=scp://hostname/dir/to/maven -DrepositoryId=fs.repo -Dfile=myarchive.jar -DgroupId=fs.work -DartifactId=myarchive -Dversion=1.0 -Dpackaging=jar
This will create dir/to/maven/fs/work/myarchive/1.0/myarchive-1.0.jar in the maven repository.

Creating a dependency
To create a dependency on myarchive.jar, add the following dependency to your pom:


  fs.work
  myarchive
  1.0

Generating Eclipse .project and .classpath files
Use the following command:
mvn eclipse:eclipse
Skipping tests
To skip tests use the property maven.test.skip=true.
mvn -Dmaven.test.skip=true install
Release a project
Two commands must be invoked, in the following order:
mvn release:prepare
mvn release:perform
Other commands
mvn install
mvn clean
mvn compile
mvn jar:jar

Tuesday, February 24, 2009

Sending Date Objects Over RMI

Had a funny issue with java Dates today which had me tearing my hair out: I was sending a java.sql.Date from one machine to another, via RMI and then storing it into the database, but the date being stored was incorrect!

The flow of the system can be described as follows:

  • CalcServer running on host A gets a "date-string" e.g. 20090220 and converts it to a java.sql.Date object using a SimpleDateFormat("yyyyMMdd")
  • CalcServer then sends the java.sql.Date object to the ResultServer, running on host B, via RMI.
  • ResultServer stores the java.sql.Date object to a java.sql.Types.DATE column in my Oracle database.

BUT, when I query my database I see 19-FEB-2009, instead of 20-FEB-2009! What is going on?

After a lot of investigation I found out that host A and host B were in different regions; one in Frankfurt and the other in London. The CalcServer in Frankfurt converts "20090220" into a date of "Fri Feb 20 00:00:00 CET 2009". When the ResultServer in London, deserialises the date it gets "Thu Feb 19 23:00:00 GMT 2009" and hence 19-FEB-2009 is stored in the database.

I have now fixed this by sending the date-string (not the java.sql.Date object) to the ResultServer, which will then parse it into a java.sql.Date object for storage.

Here is sample code which can be used to serialise a date on one host and deserialise it on another.

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class SerializationTest {

  public static void main(String[] args) throws Exception {

    final String yyyyMMdd = "20090220";
    final Date date = new SimpleDateFormat("yyyyMMdd").parse(yyyyMMdd);

    if (args.length != 1) {
      System.out.println("Usage SerializationTest S|D");
    }

    boolean serialise = false;
    if (args[0].equals("S")) {
      serialise = true;
    }
    else if (args[0].equals("D")) {
      serialise = false;
    }

    String filename = "date.ser";
    if (serialise) {
      // write the object to file
      FileOutputStream fos = new FileOutputStream(filename);
      BufferedOutputStream bos = new BufferedOutputStream(fos);
      ObjectOutputStream outputStream = new ObjectOutputStream(bos);
      outputStream.writeObject(date);
      outputStream.flush();
      outputStream.close();

      System.out.println("Serialised: " + date);
    }
    else {
      FileInputStream fis = new FileInputStream(filename);
      BufferedInputStream bis = new BufferedInputStream(fis);
      ObjectInputStream inputStream = new ObjectInputStream(bis);
      Date outDate = (Date) inputStream.readObject();
      inputStream.close();

      // print the object
      System.out.println(outDate);
    }
  }
}
Link to Stack Overflow Question