Monday, December 28, 2009

Add Shutdown Icon to Desktop

Today, I added a shutdown icon to my Dad's Windows XP desktop, to make it easier for him to turn off the computer. This is how you can do it:
  • Right-click on the desktop and choose New > Shortcut.
  • Type shutdown.exe -s -t 00 in the location box and press Next.
  • Type Turn off computer as the name of the shortcut and then press Finish.
  • Right-click the new icon that has appeared on the desktop and select Properties.
  • Click the Change Icon... button on the Shortcut tab.
  • Type %SystemRoot%\system32\SHELL32.dll in the location box and pick the Shutdown icon, which looks like a red square containing a circle with a vertical line.
Now, you can shutdown your computer by simply clicking the "Turn off computer" icon on your desktop, instead of going via the Start menu.

Adding a JProgressBar to a JTable Cell

Create a cell TableCellRenderer which uses a JProgressBar as follows:
import java.awt.Component;

import javax.swing.JProgressBar;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

public class ProgressCellRenderer extends JProgressBar
                        implements TableCellRenderer {

  /**
   * Creates a JProgressBar with the range 0,100.
   */
  public ProgressCellRenderer(){
    super(0, 100);
    setValue(0);
    setString("0%");
    setStringPainted(true);
  }

  public Component getTableCellRendererComponent(
                                    JTable table,
                                    Object value,
                                    boolean isSelected,
                                    boolean hasFocus,
                                    int row,
                                    int column) {

    //value is a percentage e.g. 95%
    final String sValue = value.toString();
    int index = sValue.indexOf('%');
    if (index != -1) {
      int p = 0;
      try{
        p = Integer.parseInt(sValue.substring(0, index));
      }
      catch(NumberFormatException e){
      }
      setValue(p);
      setString(sValue);
    }
    return this;
  }
}
Apply the cell renderer to a specific column in the table:
JTable myTable = new JTable();
TableColumn myCol = myTable.getColumnModel().getColumn(1);
myCol.setCellRenderer(new ProgressCellRenderer());
Now whenever you update a value in that column, the JProgressBar will get updated accordingly.

Sunday, December 27, 2009

Using Hermes to Browse WebLogic Topics/Queues

Hermes is a useful tool which allows you to browse JMS topics and queues. I use WebLogic as my JMS provider and it was not trivial trying to connect to my topic using Hermes, so I thought I'd post instructions to help others trying to do the same.

Here are the steps you need to take, in order to browse WebLogic queues and topics using Hermes:

1. Install HermesJMS

  • Download Hermes from here.
  • It comes as an installer jar file which you can run using the command: java -jar hermes-installer-1.13.jar.
  • Once installed, start it using hermes.bat.

2. Add WebLogic Provider
You need to add the weblogic jar to the classpath as follows:

  • On the menubar go to Actions > New > New session... to open the Preferences dialog.
  • Click on the Providers tab.
  • Right-click to Add Group and call it "weblogic92", for example.
  • Right-click on Library to Add JAR(s) and select your weblogic jar file from the file chooser dialog.
  • Select Don't Scan when prompted to scan the jar file.
  • Click Apply and close the dialog.
3. Create WebLogic Session
  • On the menubar go to Actions > New > New session... to open the Preferences dialog.
  • Click on the Sessions tab.
  • Type a name for the session. For example, "myweblogic".
  • In the Plug In list select BEA WebLogic.
  • Under Connection Factory class, pick hermes.JNDITopicConnectionFactory.
  • Select weblogic92 (defined in step 2) from the Loader dropdown.
  • Add property: initialContextFactory with a value of weblogic.jndi.WLInitialContextFactory.
  • Add property: providerURL with a value of your URL e.g. t3://myhost:2120.
  • Add property: binding with a value of the name of your connection factory e.g. myConnectionFactory.
  • Add any other properties you may have e.g. securityCredentials etc.
  • Remove all Destinations by right-clicking and selecting Remove.
  • Press OK.
4. Discover Topics/Queues
  • On the left navigation tree, go into jms > sessions > myweblogic.
  • Right-click "myweblogic" (the new session created in step 3), and click Discover. You will see a list of queues and topics appear.
  • Click on any one of them to browse.

Friday, December 25, 2009

Generics and Class.forName

This post shows how you can create objects of a specified class, using a class name and the supertype of the class you are trying to create.
public final class BeanCreator {

  /**
   * Suppress constructor.
   */
  private BeanCreator(){
  }

  /**
   * Creates an object of the class name and supertype.
   * @param <T>
   * @param className
   * @param superType
   * @return
   * @throws ClassNotFoundException
   */
  public static <T> T create(final String className,
             final Class<T> superType) throws Exception {
    final Class< ? extends T> clazz =
         Class.forName(className).asSubclass(superType);
    return create(clazz);
  }

  /**
   * Creates an object of the specified class using
   * its public or private no-arg constructor.
   *
   * @param <T>
   * @param classToCreate
   * @return
   */
  public static <T> T create(final Class<T> classToCreate)
                                      throws Exception {
        final Constructor<T> constructor =
                   classToCreate.getDeclaredConstructor();
        if (constructor == null) {
         throw new Exception("Could not create a new "+
         "instance of the dest object: " + classToCreate
         + ".  Could not find a no-arg constructor.");
        }

        // If private, make it accessible
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }
        return constructor.newInstance();
  }
}

Wednesday, October 21, 2009

Bash Globbing

Globbing refers to the expansion of shell metacharacters to complete file names. For example, when you run ls *, the shell expands the wildcard * into a list of files and passes them as arguments to ls.
sharfah@starship:~> ls *
file1 File2
Dot Globbing
By default, Bash does not glob dot-files. This means that ls * will not pick up any files beginning with the dot (.) character. However, it is easy to change this by doing the following:
sharfah@starship:~> shopt -s dotglob
sharfah@starship:~> ls *
.dotFile1 file1 File2
Case-Insensitive Globbing
There is also an option to turn on case-insensitive globbing:
sharfah@starship:~> shopt -s nocaseglob
sharfah@starship:~> ls f*
file1 File2
To view a list of all your shell options, type shopt.

Wednesday, October 14, 2009

Remote Debugging Java Applications

This is a quick post to show how you can connect to a remote Java virtual machine for debugging. I'm always forgetting the properties!

1. Add JVM Properties
Add the following properties to your java process before starting it up. This will tell it to accept debug connections.

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=4001,suspend=y
2. Connect to Remote JVM through Eclipse
  • Go to your Eclipse Debug Configurations and create a new Remote Java Application configuration.
  • In Connection Type, choose Standard (Socket Attach).
  • Set the host to the machine where your Java application is running.
  • Set the port to the debug port specified in the JVM properties e.g. 4001.
  • Press Debug to start debugging.

Friday, October 02, 2009

Using log4j's FallbackErrorHandler

Our applications currently use a DailyRollingFileAppender for logging, but since they run on NFS across a number of different servers, we quite often get errors due to stale NFS file handles, when log4j tries to write to the files. We sometimes also get errors if the logging mount point is missing on some of the servers.

I've been trying to find a way to switch to a different appender (such as a ConsoleAppender), if log4j fails to write to the log files. At first I thought of writing my own custom appender, to wrap up a FileAppender and a ConsoleAppender, and to switch to the ConsoleAppender if the FileAppender threw an IOException, but then I came across the FallbackErrorHandler, which allows you to configure a backup appender, which takes over if the primary appender fails for whatever reason.

This is how you can set up your log4j.xml file to use a FallbackErrorHandler:

1. Create a backup appender:
The backup appender will be used if the primary appender fails. My backup is a ConsoleAppender:

  <appender name="console" class="org.apache.log4j.ConsoleAppender">
    <param name="Target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d %-5p %30.30c - %m%n"/>
    </layout>
  </appender>
2. Add a FallbackErrorHandler to your primary appender:
My primary appender is a DailyRollingFileAppender. Add a FallbackErrorHandler to it and tell it to use the "console" (backup) appender, using the appender-ref tag. The root-ref tag refers to the logger that is currently using that appender. If you have a different logger use the logger-ref tag to refer to it instead.
  <appender name="file" class="org.apache.log4j.DailyRollingFileAppender">
  <errorHandler class="org.apache.log4j.varia.FallbackErrorHandler">
       <root-ref/>
       <appender-ref ref="console"/>
  </errorHandler>
    <param name="File" value="C:/temp/test.log"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d %-5p %30.30c - %m%n"/>
    </layout>
  </appender>
3. Trying it out:
To test this works, make your log file read-only, or change the path of the file to one which doesn't exist. When you run your application, you will see log4j print an error to stderr, and start logging to console, instead of file. If you turn log4j debug on you will see the message: "FB: INITIATING FALLBACK PROCEDURE." before console logging begins.

The complete log4j.xml configuration:
Here is my complete config file. (I tried setting up a log4j.properties file, but ran into problems and wasn't able to.)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %30.30c - %m%n" />
        </layout>
    </appender>
    <appender name="file" class="org.apache.log4j.DailyRollingFileAppender">
        <errorHandler class="org.apache.log4j.varia.FallbackErrorHandler">
            <root-ref />
            <appender-ref ref="console" />
        </errorHandler>
        <param name="File" value="C:/temp/test.log" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %30.30c - %m%n" />
        </layout>
    </appender>
    <root>
        <level value="INFO" />
        <appender-ref ref="file" />
    </root>
</log4j:configuration>

Wednesday, September 30, 2009

Upgrading to SyntaxHighlighter 2.0

This post is deprecated. Please read my new entry on: "Upgrading to SyntaxHighlighter 3.0"

I have now upgraded this blog to use SyntaxHighlighter 2.0. It was very easy. You don't need to make changes to any of your old posts, because this release is backwards compatible.

Another thing to note is that I had to make a change to shBrushBash.js as it wasn't formatting file redirect characters (>, <) correctly.

This is how you can upgrade too:

1. Download SyntaxHighlighter v2.0
You can download it here.
If you don't have a place to upload, you can link to my free hosted version here.

2. Link to CSS and Javascript
Open your webpage's HTML file and add links to the SyntaxHighlighter's CSS and JavaScript files. For optimal results, place these lines at the very end of your page, just before the closing body tag.

<link type="text/css" rel="stylesheet" href="http://sites.google.com/site/fahdshariff/syntaxhighlighter/styles/shCore.css"></link>
<link type="text/css" rel="stylesheet" href="http://sites.google.com/site/fahdshariff/syntaxhighlighter/styles/shThemeDefault.css"></link>
<script type="text/javascript" src="http://sites.google.com/site/fahdshariff/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="http://sites.google.com/site/fahdshariff/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="http://sites.google.com/site/fahdshariff/syntaxhighlighter/scripts/shBrushBash.js"></script>
<script type="text/javascript" src="http://sites.google.com/site/fahdshariff/syntaxhighlighter/scripts/shBrushJava.js"></script>
<script type="text/javascript" src="http://sites.google.com/site/fahdshariff/syntaxhighlighter/scripts/shBrushXml.js"></script>
<script type="text/javascript" src="http://sites.google.com/site/fahdshariff/syntaxhighlighter/scripts/shBrushSql.js"></script>
<script type="text/javascript">
    SyntaxHighlighter.config.bloggerMode = true;
    SyntaxHighlighter.config.clipboardSwf = 'http://sites.google.com/site/fahdshariff/syntaxhighlighter/scripts/clipboard.swf';
    SyntaxHighlighter.all();
    dp.SyntaxHighlighter.HighlightAll('code');
</script>
It is not necessary to add the js files for all languages - just for the ones you will be using.

3. Add Code
Now add the code you wish to highlight in your webpage, surrounded by the <pre> tag. Set the class attribute to the language alias e.g. brush:java:

<pre class="brush: java; gutter: false;">
public void printHello(){
    System.out.println("Hello World");
}
</pre>
4. View Page
View the webpage in your browser and you should see your syntax highlighted code snippet.

Links:
SyntaxHighlighter Home Page
Syntax Highlighting Code in Webpages (with SyntaxHighlighter 1.5)

Unix Wrapper Script for Java Applications

All of my Java applications are deployed onto a Unix (Linux or Solaris) environment and so I need to have a unix wrapper shell script in order to start and stop them. I looked at Java Service Wrapper but I wanted something simpler. So I wrote my own little wrapper, based on JSW.

The wrapper allows you to invoke the following set of commands on your application:

  • start: Starts up the application and writes its pid to a pid file. it also writes the hostname to the pid file. This is important, in case you try to run commands on the application on a different server. After starting, it waits a number of seconds and then checks to see if the application is still running.
  • stop: Stops the application using the kill command. If it has not died within 10 seconds, it executes the harder kill -9 command.
  • restart: Restarts the application by calling stop and then start.
  • status:Reads the pid file and invokes kill -0 on the pid to see if the process is alive. If the process is not alive, an email alert is generated, if the $EMAIL variable has been set.
  • dump: Generates a full Java thread dump by invoking kill -3 on the pid
  • purge: Purges log files
Here is the wrapper script (bin/console.ksh):
#! /bin/ksh
# A generic wrapper script to stop and start components.
# Usage: console.ksh { start | stop | restart | status | purge | dump }
#

APP_NAME="APP"
APP_LONG_NAME="MyApplication"

#the location of the pid files.
PIDDIR="../pid"
PIDFILE="$PIDDIR/$APP_NAME.pid"
TIMESTAMP=$( date +%Y%m%d_%H%M%S )

#number of seconds to wait after starting up.
WAIT_AFTER_STARTUP=2

#the location of the log files
LOG_DIR="../log"
STDOUT="$LOG_DIR/${APP_NAME}.stdout.log"

PURGE_EXPRESSION="-mtime +2"

VMARGS="-Xms250M -Xmx3000M -verbose:gc"
MAINCLASS="com.blogspot.fahdshariff.MyApplication"
ARGS="arg1 arg2"

if [ -z ${JAVA_HOME} ]
then
 echo JAVA_HOME has not been set.
 exit 1
fi

START_COMMAND="${JAVA_HOME}/bin/java $VMARGS $MAINCLASS $ARGS"

#-----------------------------------------------------------------------------
# Do not modify anything beyond this point
#-----------------------------------------------------------------------------

getpid() {
  pid=""
  if [ -f "$PIDFILE" ]
  then
    if [ -r "$PIDFILE" ]
    then
      pid=`cat "$PIDFILE"|cut -d: -f1`
      host=`cat "$PIDFILE"|cut -d: -f2`
      currhost=`hostname`
      if [ "$host" != "$currhost" ]
      then
       echo "You are on the wrong host. $APP_LONG_NAME runs on $host."
       exit 1
      fi

      if [ "X$pid" != "X" ]
      then
       kill -0 $pid > /dev/null 2>&1
  if [ $? -ne 0 ]
  then
   # Process doesn't exist, so remove pidfile
   rm -f "$PIDFILE"
            echo "Removed stale pid file: $PIDFILE"
   pid=""
  fi
      fi
    else
      echo "Cannot read $PIDFILE."
      exit 1
    fi
  fi
}

#tests if the pid is alive
testpid() {
  kill -0 $pid > /dev/null 2>&1
  if [ $? -ne 0 ]
  then
   # Process is gone so remove the pid file.
 rm -f "$PIDFILE"
 pid=""
  fi
}

start() {
  echo "Starting $APP_LONG_NAME..."
  getpid
  if [ "X$pid" = "X" ]
  then
    if [ -s $STDOUT ]
    then
     #backup current file
     mv $STDOUT $STDOUT.sav.${TIMESTAMP}
    fi
    echo CLASSPATH is $CLASSPATH > $STDOUT
    echo ${START_COMMAND} >> $STDOUT
    nohup ${START_COMMAND} >> $STDOUT 2>&1 &
    echo $!:`hostname` > $PIDFILE
  else
    echo "$APP_LONG_NAME is already running."
    exit 1
  fi

  # Sleep for a few seconds to allow for intialization if required
  # then test to make sure we're still running.
  i=0
  while [ $i -lt $WAIT_AFTER_STARTUP ]
  do
    sleep 1
    i=`expr $i + 1`
  done
  if [ $WAIT_AFTER_STARTUP -gt 0 ]
  then
    getpid
    if [ "X$pid" = "X" ]
    then
      echo "WARNING: $APP_LONG_NAME may have failed to start."
      exit 1
    else
      echo "running ($pid)."
    fi
  else
    echo ""
  fi
}


stopit() {

  echo "Stopping $APP_LONG_NAME..."
  getpid
  if [ "X$pid" = "X" ]
  then
    echo "$APP_LONG_NAME was not running."
  else
    # Running so try to stop it.
    kill $pid
    if [ $? -ne 0 ]
    then
      echo "Unable to stop $APP_LONG_NAME."
      exit 1
    fi

    #  If it has not stopped in 10 tries, forcibly kill it
    savepid=$pid
    CNT=0
    TOTCNT=0
    while [ "X$pid" != "X" ]
    do
      # Show a waiting message every 5 seconds.
      if [ "$CNT" -lt "5" ]
      then
        CNT=`expr $CNT + 1`
      else
        echo "Waiting for $APP_LONG_NAME to exit..."
        CNT=0
      fi

      if [ $TOTCNT -gt 11 ]
      then
       echo "Killing by force (kill -9)"
       kill -9 $pid
      fi

      TOTCNT=`expr $TOTCNT + 1`
      sleep 1

      testpid
    done

    pid=$savepid
    testpid
    if [ "X$pid" != "X" ]
    then
      echo "Failed to stop $APP_LONG_NAME."
      exit 1
    else
      echo "Stopped $APP_LONG_NAME."
    fi
  fi
}

purge() {
  pcmd="find $LOG_DIR -follow -type f $PURGE_EXPRESSION -print -exec rm -f {} +"
  echo "Running purge: $pcmd"
  $pcmd
}

alert() {
  if [ "X$EMAIL" != "X" ]
  then
   echo "$APP_LONG_NAME is not running on `hostname`. Please check and restart." \
   | mailx -s "`hostname`: $APP_LONG_NAME is not running" "$EMAIL"
   echo "Sent alert to $EMAIL"
  fi
}

status() {
  getpid
  if [ "X$pid" = "X" ]
  then
    echo "$APP_LONG_NAME is not running."
    alert
    exit 1
  else
    echo "$APP_LONG_NAME is running (PID:$pid)."
    exit 0
  fi
}

dump() {
  echo "Dumping $APP_LONG_NAME..."
  getpid
  if [ "X$pid" = "X" ]
  then
    echo "$APP_LONG_NAME is not running."
  else
    kill -3 $pid

    if [ $? -ne 0 ]
    then
      echo "Failed to dump $APP_LONG_NAME."
      exit 1
    else
      echo "Dumped $APP_LONG_NAME."
    fi
  fi
}

####################

case "$1" in

  'start')
    start
    ;;

  'stop')
    stopit
    ;;

  'restart')
    stopit
    start
    ;;

  'status')
    status
    ;;

  'dump')
    dump
    ;;

  'purge')
    purge
    ;;

  *)
    echo "Usage: $0 { start | stop | restart | status | dump | purge }"
    exit 1
    ;;
esac

exit 0

Tuesday, September 29, 2009

Bash: Convert String to Array

This is how you can convert a string into an array:
s="124890"
for i in $(seq 0 $((${#s}-1)))
do
     arr[$i]=${s:$i:1}
done
${#s} refers to the length of the string s, which in this case is 6.
${s:$i:1} returns a substring of s, starting from $i, of length 1.

The seq command is a new one for me. Unfortunately, it doesn't seem to be available on Solaris, only Linux. From the man pages:

NAME
       seq - print a sequence of numbers

SYNOPSIS
       seq [OPTION]... LAST
       seq [OPTION]... FIRST LAST
       seq [OPTION]... FIRST INCREMENT LAST
To see how you can convert a string of digits to an array in other languages, read this stackoverflow question.

Monday, September 28, 2009

Double Brace Initialisation

"Double Brace Initialisation" is one of the lesser known features in Java. It can be useful for initialising collections that have to be passed to other methods.

For example, without double-brace initialisation:

Map<String,String> map = new HashMap <String,String>();
map.put("key","value");
map.put("key2","value2");
printMap(map);
With double-brace initialisation:
printMap(new HashMap <String,String>(){{
            put("key","value");
            put("key2","value2");
        }});
The first brace creates a new AnonymousInnerClass, the second declares an instance initializer block that is run when the anonymous inner class is instantiated. This only works only for non-final classes because it creates an anonymous subclass.

Don't go too overboard with this because it creates a new anonymous inner class just for making a single object which might not be very efficient! It will also create an additional class file on disk.

Another example:

//double-brace initialisation
List<String> list = new ArrayList<String>(){{
   add("apple");
   add("banana");
}};

//better alternative
List<String> list2 = Arrays.asList("apple","banana");

Saturday, August 29, 2009

Speed up Eclipse

I've just noticed a big performance improvement in Eclipse 3.5 (Galileo), just by launching it with the latest JRE (1.6.0_16) and unlocking the new G1 garbage collector. My startup time has reduced from about 30 secs to 4-5 secs and the interface is snappier.

Here is my eclipse.ini:

-startup
plugins/org.eclipse.equinox.launcher_1.0.200.v20090520.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.0.200.v20090519
-product
org.eclipse.epp.package.java.product
-data
c:\eclipse
-showlocation
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
384m
-vm
C:\program files\Java\jdk1.6.0_16\jre\bin\client\jvm.dll
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms128m
-Xmx384m
-Xss4m
-XX:PermSize=128m
-XX:MaxPermSize=128m
-XX:CompileThreshold=5
-XX:MaxGCPauseMillis=10
-XX:MaxHeapFreeRatio=70
-XX:+UnlockExperimentalVMOptions
-XX:+UseG1GC
-XX:+UseFastAccessorMethods
-XX:+AggressiveOpts
-Dcom.sun.management.jmxremote
Let me know if there are any other improvements I can make!

Tuesday, August 18, 2009

Cron Jobs Not Launching

We had an issue recently with cron jobs not firing on our Solaris 10 server. We finally tracked it down to cron not being initialised when the server rebooted. The error was in a file called /var/svc/log/system-cron:default.log and indicated that it couldn't find the user. Possibly due to NFS not being mounted.
sharfah@starship:~> tail -10 /var/svc/log/system-cron:default.log
[ Aug 16 00:01:25 Stopping because service disabled. ]
[ Aug 16 00:01:25 Executing stop method (:kill) ]
[ Aug 16 00:07:27 Enabled. ]
[ Aug 16 00:07:28 Executing start method ("/lib/svc/method/svc-cron") ]
[ Aug 16 00:07:28 Method "start" exited with status 0 ]
! No such user as sharfah - cron entries not created Sun Aug 16 00:07:28 2009
We then got the Unix SAs to restart svc:/system/cron:default:
sharfah@starship:~> tail -10 /var/svc/log/system-cron:default.log
[ Aug 18 17:25:19 Stopping because service restarting. ]
[ Aug 18 17:25:19 Executing stop method (:kill) ]
[ Aug 18 17:25:20 Executing start method ("/lib/svc/method/svc-cron") ]
[ Aug 18 17:25:20 Method "start" exited with status 0 ]
Another way to fix this issue, is to edit and resave crontab.

For debugging cron issues, a useful tip is to look at /var/mail/user which will contain any stdout or errors from your cron jobs.

Related post:
Percent Sign in Crontab

Monday, August 17, 2009

Retrying Operations in Java

There are many cases in which you may wish to retry an operation a certain number of times. Examples are database failures, network communication failures or file IO problems.

Approach 1
This is the traditional approach and involves a counter and a loop.

final int numberOfRetries = 5 ;
final long timeToWait = 1000 ;

for (int i=0; i<numberOfRetries; i++) {
 //perform the operation
 try {
  Naming.lookup("rmi://localhost:2106/MyApp");
  break;
 }
 catch (Exception e) {
  logger.error("Retrying...",e);
  try {
   Thread.sleep(timeToWait);
  }
  catch (InterruptedException i) {
  }
 }
}
Approach 2
In this approach, we hide the retry counter in a separate class called RetryStrategy and call it like this:
public class RetryStrategy
{
 public static final int DEFAULT_NUMBER_OF_RETRIES = 5;
 public static final long DEFAULT_WAIT_TIME = 1000;

 private int numberOfRetries; //total number of tries
 private int numberOfTriesLeft; //number left
 private long timeToWait; //wait interval

 public RetryStrategy()
 {
  this(DEFAULT_NUMBER_OF_RETRIES, DEFAULT_WAIT_TIME);
 }

 public RetryStrategy(int numberOfRetries, long timeToWait)
 {
  this.numberOfRetries = numberOfRetries;
  numberOfTriesLeft = numberOfRetries;
  this.timeToWait = timeToWait;
 }

 /**
  * @return true if there are tries left
  */
 public boolean shouldRetry()
 {
  return numberOfTriesLeft > 0;
 }

 /**
  * This method should be called if a try fails.
  *
  * @throws RetryException if there are no more tries left
  */
 public void errorOccured() throws RetryException
 {
  numberOfTriesLeft --;
  if (!shouldRetry())
  {
   throw new RetryException(numberOfRetries +
     " attempts to retry failed at " + getTimeToWait() +
     "ms interval");
  }
  waitUntilNextTry();
 }

 /**
  * @return time period between retries
  */
 public long getTimeToWait()
 {
  return timeToWait ;
 }

 /**
  * Sleeps for the duration of the defined interval
  */
 private void waitUntilNextTry()
 {
  try
  {
   Thread.sleep(getTimeToWait());
  }
  catch (InterruptedException ignored) {}
 }

 public static void main(String[] args) {
  RetryStrategy retry = new RetryStrategy();
  while (retry.shouldRetry()) {
   try {
    Naming.lookup("rmi://localhost:2106/MyApp");
    break;
   }
   catch (Exception e) {
    try {
     retry.errorOccured();
    }
    catch (RetryException e1) {
     e.printStackTrace();
    }
   }
  }
 }
}
Approach 3
Approach 2, although cleaner, hasn't really reduced the number of lines of code we have to write. In the next approach, we hide the retry loop and all logic in a separate class called RetriableTask. We make the operation that we are going to retry Callable and wrap it in a RetriableTask which then handles all the retrying for us, behind-the-scenes:
public class RetriableTask<T> implements Callable<T> {

 private Callable<T> task;
 public static final int DEFAULT_NUMBER_OF_RETRIES = 5;
 public static final long DEFAULT_WAIT_TIME = 1000;

 private int numberOfRetries; // total number of tries
 private int numberOfTriesLeft; // number left
 private long timeToWait; // wait interval

 public RetriableTask(Callable<T> task) {
  this(DEFAULT_NUMBER_OF_RETRIES, DEFAULT_WAIT_TIME, task);
 }

 public RetriableTask(int numberOfRetries, long timeToWait,
                      Callable<T> task) {
  this.numberOfRetries = numberOfRetries;
  numberOfTriesLeft = numberOfRetries;
  this.timeToWait = timeToWait;
  this.task = task;
 }

 public T call() throws Exception {
  while (true) {
   try {
    return task.call();
   }
   catch (InterruptedException e) {
    throw e;
   }
   catch (CancellationException e) {
    throw e;
   }
   catch (Exception e) {
    numberOfTriesLeft--;
    if (numberOfTriesLeft == 0) {
     throw new RetryException(numberOfRetries +
     " attempts to retry failed at " + timeToWait +
     "ms interval", e);
    }
    Thread.sleep(timeToWait);
   }
  }
 }

 public static void main(String[] args) {
  Callable<Remote> task = new Callable<Remote>() {
   public Remote call() throws Exception {
    String url="rmi://localhost:2106/MyApp";
    return (Remote) Naming.lookup(url);
   }
  };

  RetriableTask<Remote> r = new RetriableTask<Remote>(task);
  try {
   r.call();
  }
  catch (Exception e) {
   e.printStackTrace();
  }
 }
}
Also see: References:

Tuesday, August 04, 2009

Setting up Tortoise CVS with CollabNet

We recently moved from Sourceforge to CollabNet and had to make changes to our CVS client setup. Sourceforge uses "pserver", whereas Collabnet uses "ext ssh". This is how you can get Tortoise CVS to connect to CollabNet, without prompting you for passwords every time.

1. Create a pair of public/private keys
You can create a pair of keys using PuTTY Key Generator (PUTTYGEN.EXE). Simply press the Generate button and move your mouse to generate randomness. This will create a public key for pasting into an authorized_keys file. Press the Save public key and Save private key buttons to save the keys as files. I have saved mine as:

  • C:\Documents and Settings\[xpid]\ssh\id_dsa (private)
  • C:\Documents and Settings\[xpid]\ssh\id_dsa.pub (public)
Alternatively, you can use the UNIX utility "ssh-keygen", as explained in one of my previous posts here.

2. Store your public key on CollabNet

  • Log onto CollabNet
  • Click on My Workspace
  • Click on Authorization Keys
  • Paste the public key, generated in Step 1, into the Authorized Keys text area and press Update
3. Update Tortoise SSH Settings
  • Open TortoiseCVS Preferences
  • Go to the Tools tab
  • Add your private key to your SSH parameters, so that it becomes:
-i "C:\Documents and Settings\[xpid]\ssh\id_dsa" -l "%u" "%h"
Now, whenever you perform a CVS operation through Tortoise, it will not prompt you for a password.

Your CVSROOT should look something like this:

:ext:fahd_shariff@cvs-collabnet.server.com:/cvsroot/module
References:
Tortoise FAQs
CollabNet Help

Monday, July 27, 2009

Howto: Import Certificates into a Keystore

One night, our Java application, which connects to a webservice, started failing with the following error:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at org.apache.axis.AxisFault.makeFault(AxisFault.java:101)
at org.apache.axis.transport.http.HTTPSender.invoke(HTTPSender.java:154)
at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:165)
at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
at org.apache.axis.client.Call.invoke(Call.java:2767)
at org.apache.axis.client.Call.invoke(Call.java:2443)
at org.apache.axis.client.Call.invoke(Call.java:2366)
at org.apache.axis.client.Call.invoke(Call.java:1812)
This error meant that our application did not have a valid certificate, but since our application had been working fine for the past few months, the only plausible explanation was that the webservice that we were trying to connect to, had changed their certificate without telling us!

I then had to go about getting hold of the new certificate and importing it into my truststore, in order to get my application up and running again. This is how:

1) Save the SSL Certificate to a File
In Firefox 3.5 (it's easier):

  • Open the webservice url
  • Double-click the padlock icon (or right-click on page and select Page Info)
  • Click on the Security tab (the padlock icon)
  • Press View Certificate
  • Click on the Details tab
  • Press Export...
  • Choose a file to save to - I like to save as type: X.509 Certification (DER)
In Internet Explorer (IE 8):
  • Open the webservice url
  • Click the padlock icon and then on View Certificates
  • Click on Install Certificate, click Next
  • Choose Place all certificates in the following store and Browse to Personal
  • Click Next and run through the rest of the screens
  • Go to Start > Run > certmgr.msc
  • Select Personal
  • Right click on certificate, go to All Tasks > Export...
Once saved, you can view the certificate using Java Keytool as follows:
keytool -printcert -file mycert.cer

2) Import Certificate to Keystore
Now that we have saved the website certificate to a local file, we can use Java Keytool to import it into our keystore using the following command:

keytool -import -alias myalias -file mycert.cer \
        -keystore mytruststore
You can also display the contents of the keystore using the following command:
keytool -list -v -keystore mytruststore

Thursday, July 16, 2009

Enabling Desktop SSO in Firefox

This is how you can configure Firefox to use Desktop Single Sign On (SSO) / Kerberos authentication:
  • Go to about:config
  • Change your preference network.negotiate-auth.delegation-uris
  • to the domain you want to authenticate against, for example ".domain.com".
  • Change your preference network.negotiate-auth.trusted-uris
  • to the domain as above.
Now try going to a URL and you should be able to login automatically. This has been tried and tested with Firefox 3.5.

Friday, June 26, 2009

Fibonacci Shell Script

Here is a quick unix shell script which prints out the Fibonacci sequence:

0,1,1,2,3,5,8,13,21,34,55,89,144,...

The first two Fibonacci numbers are 0 and 1, and each remaining number is the sum of the previous two.

#!/bin/sh
prev=0
next=1

echo $prev

while(true)
do
 echo $next

 #add the two numbers
 sum=$(($prev+$next))

 #swap
 prev=$next
 next=$sum
 sleep 1
done

Monday, June 01, 2009

Using XPath in Java

Given the following xml document:
<hosts>
  <host name="starship" port="8080"/>
  <host name="firefly" port="8180"/>
</hosts>
this is how you can use the javax.xml.xpath library to run an XPath query in order to obtain a list of host names:
//create a document
DocumentBuilderFactory domFactory = 
                     DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("file.xml");

//create the xpath expression
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//host/@name");

//run the xpath query
Object result = expr.evaluate(doc, XPathConstants.NODESET);

//read results
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
    System.out.println(nodes.item(i).getNodeValue());
}

Thursday, May 21, 2009

find -exec vs xargs

If you want to execute a command on lots of files found by the find command, there are a few different ways this can be achieved (some more efficient than others):

-exec command {} \;
This is the traditional way. The end of the command must be punctuated by an escaped semicolon. The command argument {} is replaced by the current path name found by find. Here is a simple command which echoes file paths.

sharfah@starship:~> find . -type f -exec echo {} \;
.
./1.txt
./2.txt
This is very inefficient, because whenever find finds a file, it forks a process for your command, waits for this child process to complete and then searches for the next file. In this example, you will get the following child processes: echo .; echo ./1.txt; echo ./2.txt. So if there are 1000 files, there are 1000 child processes and find waits.

-exec command {} +
If you use a plus (+) instead of the escaped semicolon, the arguments will be grouped together before being passed to the command. The arguments must be at the end of the command.

sharfah@starship:~> find . -type f -exec echo {} +
. ./1.txt ./2.txt
In this case, only one child process is created: echo . ./1.txt ./2.txt, which is much more efficient, because it avoids a fork/exec for each single argument.

xargs
This is similar to the approach above, in that files found are bundled up (usually in batches of about 20-50 names) and sent to the command as few times as possible. find doesn't wait for your command to finish.

sharfah@starship:~> find . -type f | xargs echo
. ./1.txt ./2.txt
This approach is efficient and works well as long as you do not have funny characters (e.g. spaces) in your filenames as they won't be escaped.

Performance Testing
So which one of the above approaches is fastest? I ran a test across a directory with 10,000 files out of which 5,600 matched my find pattern. I ran the test 10 times, changing the order of the finds each time, but the results were always the same. xargs and + were very close, with \; always finishing last. Here is one result:

time find . -name "*20090430*" -exec touch {} +
real    0m31.98s
user    0m0.06s
sys     0m0.49s

time find . -name "*20090430*" | xargs touch
real    1m8.81s
user    0m0.13s
sys     0m1.07s

time find . -name "*20090430*" -exec touch {} \;
real    1m42.53s
user    0m0.17s
sys     0m2.42s
I'm going to be using the -exec command {} + method, because it is faster and can handle my funny filenames.

Saturday, May 09, 2009

Percent Sign in Crontab

From the man pages of crontab:

The sixth field of a line in a crontab file is a string that is executed by the shell at the specified times. A percent character in this field (unless escaped by \) is translated to a NEWLINE character.

Only the first line (up to a `%' or end of line) of the command field is executed by the shell. Other lines are made available to the command as standard input. Any blank line or line beginning with a `#' is a comment and is ignored.

This means that you need to escape any percent (%) characters. For example, I have a daily backup cron which writes the current crontab to a backup file every morning, and I have to escape the date command, as shown below:

01 07 * * * crontab -l > /home/user/cron.`date +\%Y\%m\%d`
Also note, that cron isn't clever enough to expand the tilde (~) character, so always use the full path to your home directory.

If you find that a cron hasn't fired, check your email in /var/mail/user.

Friday, May 08, 2009

Solaris - CPU, Memory and Version

CPU Info:
In order to find information about processors on Solaris, use the psrinfo command:
sharfah@starship:~> psrinfo -v
Status of virtual processor 0 as of: 05/08/2009 09:53:17
  on-line since 05/03/2009 00:05:06.
  The i386 processor operates at 2612 MHz,
        and has an i387 compatible floating point processor.
Status of virtual processor 1 as of: 05/08/2009 09:53:17
  on-line since 05/03/2009 00:05:12.
  The i386 processor operates at 2612 MHz,
        and has an i387 compatible floating point processor.
Status of virtual processor 2 as of: 05/08/2009 09:53:17
  on-line since 05/03/2009 00:05:14.
  The i386 processor operates at 2612 MHz,
        and has an i387 compatible floating point processor.
Status of virtual processor 3 as of: 05/08/2009 09:53:17
  on-line since 05/03/2009 00:05:16.
  The i386 processor operates at 2612 MHz,
        and has an i387 compatible floating point processor.
Status of virtual processor 4 as of: 05/08/2009 09:53:17
  on-line since 05/03/2009 00:05:18.
  The i386 processor operates at 2612 MHz,
        and has an i387 compatible floating point processor.
Status of virtual processor 5 as of: 05/08/2009 09:53:17
  on-line since 05/03/2009 00:05:20.
  The i386 processor operates at 2612 MHz,
        and has an i387 compatible floating point processor.
Status of virtual processor 6 as of: 05/08/2009 09:53:17
  on-line since 05/03/2009 00:05:22.
  The i386 processor operates at 2612 MHz,
        and has an i387 compatible floating point processor.
Status of virtual processor 7 as of: 05/08/2009 09:53:17
  on-line since 05/03/2009 00:05:24.
  The i386 processor operates at 2612 MHz,
        and has an i387 compatible floating point processor.
Memory Info:
In order to find out how much physical memory is installed, use prtconf:
sharfah@starship:~> prtconf | grep Memory
Memory size: 65536 Megabytes
Version Info:
To show machine, software revision and patch revision information use the showrev command:
sharfah@starship:~> showrev
Hostname: starship
Hostid: 80f32709
Release: 5.10
Kernel architecture: i86pc
Application architecture: i386
Hardware provider:
Kernel version: SunOS 5.10 Generic_137112-06
sharfah@starship:~> uname -a
SunOS starship 5.10 Generic_137112-06 i86pc i386 i86pc
Processes:
In order to list the processes running, use prstat (equivalent to top).
sharfah@starship:~> prstat
  PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/NLWP
 4049 sharfah   1008K  840K sleep    0    0   0:03.17 0.3% find/1
14632 sharfah      114M   68M sleep   29   10   1:19.18 0.1% java/30
Related posts:
Linux - CPU, Memory and Version

Thursday, May 07, 2009

Creating a Report with SQLPlus

For those of you who have used SQL*Plus, you will know that it is a nightmare to get the output looking just the way you want it to. You have to battle with page sizes and column widths. (Why isn't there an option to set the column size automatically, I wonder?)

Here are a few things that I have learnt:

Silent Mode
Use the -s flag on your sqlplus command in order to inhibit output such as the SQL*Plus banner and prompt.

Spooling to a file
You need to spool in order to write the output of your sql commands to a file. Turn it off when you are done.

SQL> spool results.out
SQL> select 1 from dual;
SQL> spool off

Page Size
This refers to the number of rows on a single page. The default is 14 which means that after 14 lines, your table header will be repeated, which is ugly! In order to get around this, set your page size to the maximum of 50000. It would be nice if you could set it to unlimited.

SQL> show pagesize;
pagesize 24
SQL> set pagesize 50000
Line Size
This refers to how long your line can get before it wraps to the next line. If you are not sure how long your line can be, set the size to the maximum of 32767 and turn on trimspool in order to remove trailing blanks from your spooled file.
SQL> show linesize;
pagesize 80
SQL> set linesize 32767
SQL> set trimspool on
Column Size
You can specify the size of individual columns like this:
SQL> col employee_name format a40
Titles
Use TTITLE to display a heading before you run a query.
SQL> TTITLE LEFT 'My table heading'
SQL> select 1 from dual;

My table heading
         1
----------
         1
Use SKIP to skip lines e.g. SKIP 2 would be equivalent to pressing Return twice.
SQL> ttitle left 'My table heading' -
> SKIP 2 'Another heading' SKIP 2
SQL> select 1 from dual;

My table heading

Another heading

         1
----------
         1
Example script
The shell script below uses SQL*Plus to create a report.
#! /usr/bin/sh

#the file where sql output will go
OUT=/report/path/report.txt

#email this report?
EMAIL=Y

#oracle variables
ORACLE_HOME=/path/oracle/client
export ORACLE_HOME
SQLPLUS=$ORACLE_HOME/bin/sqlplus
export SQLPLUS
LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
TNS_ADMIN=/path/tnsnames
export TNS_ADMIN

#######################
#sqlplus - silent mode
#redirect /dev/null so that output is not shown on terminal
$SQLPLUS -s "user/pass@database" << END_SQL > /dev/null

SET ECHO OFF
SET TERMOUT OFF

SET PAGESIZE 50000

SET LINESIZE 32767
SET TRIMSPOOL ON

COL EMPLOYEE_NAME FORMAT A40

SPOOL $OUT

TTITLE LEFT 'EMPLOYEE REPORT' -
SKIP 2 LEFT 'Number of Employees:' SKIP 2

SELECT COUNT(*) AS total FROM employee
/

TTITLE LEFT 'Employee Names' SKIP 2

SELECT employee_name FROM employee
ORDER BY employee_name DESC
/

SPOOL OFF

END_SQL
#######################

#change tabs to spaces
expand $OUT > $OUT.new
mv $OUT.new $OUT

echo Finished writing report $OUT

if [ "$EMAIL" = "Y" ]
then
 to=someone@abc.com
 subject="Employee Report"
 mailx -s "$subject" $to < $OUT
 echo "Emailed report"
fi

Wednesday, May 06, 2009

Maven Release

Prepare the release
Run the following command:
mvn release:prepare
This command will prompt you for a release version name, the next version name and will tag the code in CVS.

For example, if the current version in your pom is 1_10-SNAPSHOT, after running release:prepare, the version will be changed to 1_10, maven will commit (with a comment of [maven-release-plugin] prepare release myapp-1_10), tag as myapp-1_10, bump the pom version to 1_11-SNAPSHOT and commit it (with a comment of [maven-release-plugin] prepare for next development iteration).

release:prepare will also create a file called release.properties, shown below:

maven.username=sharfah
checkpoint.transformed-pom-for-release=OK
scm.tag=myapp-1_10
scm.url=scm:cvs:pserver::@sourceforge.uk.db.com:/data/cvsroot/apps:MyApp
checkpoint.transform-pom-for-development=OK
checkpoint.local-modifications-checked=OK
checkpoint.initialized=OK
checkpoint.checked-in-release-version=OK
checkpoint.tagged-release=OK
checkpoint.prepared-release=OK
checkpoint.check-in-development-version=OK
Perform the release
Run the following command:
mvn release:perform
This will use the release.properties file in order to check-out the tagged version from source control, compile, test and deploy it to the maven repository. If you have deleted your release.properties file, don't worry, you can just create a dummy one yourself, using the sample above.

If you want to skip site-deploy run the following command instead:

mvn release:perform -Dgoals=deploy
Related posts:
Quick Maven Commands
Skip Tests in Maven

Tuesday, April 28, 2009

Setup Samba on Ubuntu

Here is a quick guide to setting up a Samba share on Ubuntu Linux.

Install the package

sudo apt-get install samba smbfs
Edit smb.conf
Open the samba configuration file in your favourite editor, change security to user and add a username map.
sudo vi /etc/samba/smb.conf
# "security = user" is always a good idea. This will require a Unix account # in this server for every user accessing the server. See # /usr/share/doc/samba-doc/htmldocs/Samba3-HOWTO/ServerType.html # in the samba-doc package for details. security = user username map = /etc/samba/smbusers
Create a samba user
sudo smbpasswd -a fahd
Add the new user to the smbusers file
The format is "unix username" = "samba username".
sudo vi /etc/samba/smbusers
fahd = fahd
Share home directory
Make the following changes to smb.conf in order to share your home directory over samba and make it writable.
sudo vi /etc/samba/smb.conf
#======================= Share Definitions ======================= # Un-comment the following (and tweak the other settings below to suit) # to enable the default home directory shares. This will share each # user's home directory as \\server\username [homes] comment = Home Directories browseable = no # By default, the home directories are exported read-only. Change the # next parameter to 'no' if you want to be able to write to them. read only = no # By default, \\server\username shares can be connected to by anyone # with access to the samba server. Un-comment the following parameter # to make sure that only "username" can connect to \\server\username # This might need tweaking when using external authentication schemes valid users = %S
Connecting to the samba
Now you should be able to map a drive on windows using the following share format: \\ubuntumachine\username. The first time you will be prompted for a username and password.

Restarting samba

sudo /etc/init.d/samba restart

Tuesday, March 31, 2009

100th post: Using Google Charts

This is my 100th blog entry! I would like to take this opportunity to thank everyone who has stopped by to read, comment or share something on this blog and make it a success. I started fahd.blog in August 2006 mainly to document my experiences with new technologies and also to share tips and tricks that I have learnt with the rest of the world. I don't plan to stop here, so stay tuned for more interesting posts!

In this post, I'm going to be using the Google Chart API to dynamically create charts illustrating the frequency of my blog posts over the last few years. This API allows you to specify all your data within a URL and have Google create the chart image for you.

Bar Chart
I'm going to draw a bar chart to show the number of posts per month, since 2006.
The chart parameters I will use are:

  • cht=bhs (horizontal bar chart)
  • chs=400x600 (size)
  • chd=t:5,6,2,2,2,2,3,7,... (data)
  • chds=0,10 (data scale - min/max)
  • chxt=x,y,x (axes)
  • chxl=0:|0|1|...|1:|Aug-06|Sep-06|...|2:||Number| (axis labels)
  • chco=76A4FB (colour)
  • chtt=Blog Posts by Month (title)
This generates the following image:

<img src="http://chart.apis.google.com/chart?cht=bhs&chbh=a&chs=400x600&chd=t:5,6,2,2,2,2,3,7,10,9,3,5,0,1,0,1,0,2,2,1,0,1,0,0,4,5,5,4,7,2,2,7&chds=0,10&chxt=x,y&chxl=0:|0|1|2|3|4|5|6|7|8|9|10|1:|Aug-06|Sep-06|Oct-06|Nov-06|Dec-06|Jan-07|Feb-07|Mar-07|Apr-07|May-07|Jun-07|Jul-07|Aug-07|Sep-07|Oct-07|Nov-07|Dec-07|Jan-08|Feb-08|Mar-08|Apr-08|May-08|Jun-08|Jul-08|Aug-08|Sep-08|Oct-08|Nov-08|Dec-08|Jan-09|Feb-09|Mar-09&chtt=Blog Posts by Month&chco=76A4FB"/>

Line Chart
Here's the same data put into a line chart.
The chart parameters I will use are:

  • cht=lc (line chart)
  • chs=400x300 (size)
  • chd=t:7,2,2,7,4,5,... (data)
  • chds=0,10 (data scale - min/max)
  • chxt=y,x (axes)
  • chxl=0:|0|1|...|1:|Aug-06|... (axis labels)
  • chco=80C65A (colour)
  • chm=o,FF0000,0,-1,5.0|V,3399CC,0,23,0.5 (circle each point; vertical line at peak)
  • chtt=Blog Posts by Month (title)
This generates the following image:

<img src="http://chart.apis.google.com/chart?cht=lc&chbh=a&chs=400x300&chco=80C65A&chds=0,10&chd=t:7,2,2,7,4,5,5,4,0,0,1,0,1,2,2,0,1,0,1,0,5,3,9,10,7,3,2,2,2,2,6,5&chxt=y,x&chxl=0:|0|1|2|3|4|5|6|7|8|9|10|1:|Aug-06|Apr-07|Nov-07|Jul-08|Mar-09&chm=o,FF0000,0,-1,5.0|V,3399CC,0,23,0.5&chtt=Blog Posts by Month"/>

Pie Chart
I'm going to use a Pie Chart to show the number of posts per year.
The chart parameters I will use are:

  • cht=p3 (3D pie chart)
  • chs=270x120 (size)
  • chd=t:22,21,44,13 (data)
  • chl=2006|2007|2008|2009 (label)
  • chtt=Blog Posts by Year (title)
This generates the following image:

<img src="http://chart.apis.google.com/chart?cht=p3&chs=270x120&chd=t:22,21,44,13&chl=2006|2007|2008|2009&chtt=Blog Posts by Year"/>

As can be seen above, the Chart API is very powerful and allows you to produce different types of charts and customise them with different styles. However, in my opinion, the API is not intuitive and the URLs are quite cryptic. I doubt I will remember how to use the API without having it open in front of me!

References:
Google Chart API

Monday, March 30, 2009

Named Pipes with mkfifo [Unix]

Named pipes are useful for inter-process communication. Unlike anonymous pipes, any number of readers and writers can use a named pipe. They are very useful for letting other processes know that something has happened e.g. a file has been created etc.

Creating a named pipe
You can create a named pipe using the mkfifo command, which creates a special pipe file that remains in place until it is removed. Since it is a type of file, you can use the rm command to remove it when you are done.

sharfah@firefly:~> mkfifo mypipe
sharfah@firefly:~> ls
mypipe|
Writing to a named pipe
Since a named pipe is just a special type of file, you can write to it just as you would normally write to a file. However, if there are no processes reading from the pipe, the write call will block.

The following example script writes numbers into the pipe. If there are no readers, it will block on line 5.

COUNT=1
while (true)
do
 echo Writer$$: $COUNT
 echo $COUNT > mypipe
 COUNT=`expr $COUNT + 1`
 sleep 1
done
Reading from a named pipe
Reading from a named pipe is the same as reading from a normal file. You can cat a named pipe, tail it or read it as follows:
while (true)
do
   read line < mypipe
   echo Reader$$: $line
done
Multiple readers
If you have multiple readers reading from the same pipe, only one of the readers will receive the output. This is illustrated with the following example, in which I have launched one writer and two readers:
sharfah@firefly:~> writer.sh& reader.sh& reader.sh&
Writer10500: 1 
Reader10501: 1 
Writer10500: 2 
Reader10502: 
Reader10501: 2 
Writer10500: 3 
Reader10501: 3 
Reader10502: 
Writer10500: 4 
Reader10501: 
Reader10502: 4 
Writer10500: 5 
Reader10502: 5 
Reader10501: 
Writer10500: 6 
Reader10501: 6 
Reader10502: 
Writer10500: 7 
Reader10502: 7 
Reader10501: 
Writer10500: 8 
Reader10502: 8 
Reader10501: 
Writer10500: 9 
Reader10501: 9 
Reader10502: 
Writer10500: 10 
Reader10502: 10 

Friday, March 20, 2009

Vertical Rush Results

Yesterday, I climbed Tower 42 with my team, "Are we there yet?". We got there at 11.30am, wearing our black, Vertical Rush t-shirts and our race numbers pinned on. Our chip timers were tied to our shoes. Then the race started. They let us off in small batches so that we wouldn't clog the stairwell. I started off quick, taking two stairs at a time, but by the time I got halfway, I felt my energy running out. I could hear my heart pounding in my chest and I changed to taking single stairs and dragging myself up using the banisters.

I finally made it to the top and staggered into the lounge where I was given a "goodie" bag containing a bottle of water, a disgusting banana energy gel and a Tower 42 mouse mat! There were no chairs, so I slumped onto the floor and enjoyed my water. It has never tasted so good!

We then had some team photos taken at the top, which I now have to buy! Sadly, they didn't allow any cameras or mobile phones into the race, so I wasn't able to take any pictures myself.

In the evening, there was a prize-giving ceremony at Gibson Hall where we found out our times and got a certificate for taking part. Nearly 600 people took part in the race and the fastest time was 00:04:22!

My Stats
Race Number: 5034
Gender Position: 341
Category: Financial
Category Position: 100
Club: Deutsche Bank
Chip Time: 00:09:10
Chip Position: 382

I'm also really proud about how much we raised for Shelter! Here is our Just Giving page: http://www.justgiving.com/reteamstairclimb

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

Friday, February 20, 2009

Java Serialization Snippets

Object serialisation is the process of saving an object's state to a sequence of bytes. De-serialisation is the process of rebuilding the object from those bytes later.

The object to be serialised must implement the Serializable interface or inherit it. If you don't want fields in the object serialised mark them as transient.

Serializing an Object

//the object to serialize
Date date=new Date() ;

//the file to serialize to
String filename="date.ser";

//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();
De-Serializing an Object

//the file containing the serialized object
String filename="date.ser";

//read the object from the file
FileInputStream fis=new FileInputStream(filename);
BufferedInputStream bis=new BufferedInputStream(fis);
ObjectInputStream inputStream=new ObjectInputStream(bis);
Date date=(Date)inputStream.readObject();
inputStream.close();

//print the object
System.out.println(date);
Determining the Size of an Object
//the object to measure
Date date=new Date() ;

//write it out to a byte array
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(date);
oos.close();
byte[] ba=baos.toByteArray();
baos.close();

//print size
System.out.println(ba.length);
or you can serialize the object to a file and measure the size of the file.

Friday, February 13, 2009

Happy 1234567890!

The epoch timestamp just passed 1234567890 and I saw it change!

Epoch Time

The time has come.

Tonight, at exactly 23:31:30 UTC, Epoch Time will reach 1234567890! You can watch the countdown here: http://coolepochcountdown.com/

Epoch Time (or Unix Time) can be defined as the number of seconds elapsed since 1 January 1970 00:00:00, not counting leap seconds. It is widely used on many computing systems.

We had an interesting time at work today, coming up with programs in different languages to watch Epoch Time tick by. Here are some of them:

Shell

 while true; do date +%s; sleep 1; done
Perl
 perl -le 'while(true){print time();sleep 1;}'
Haskell
import System.Time
import System.Posix

main :: IO ()
main = do { TOD epoch _ <- getClockTime
       ; putStrLn $ show epoch
       ; threadDelay 1000000
       ; main }
Groovy
groovy -e "while(true){println(new Date().time);sleep(1000)}"
Python
python -c \
  "while True: import time;print time.time();time.sleep(1)"
Java
import java.util.Date;

public class EpochTime {
  public static void main(String[] args) {
    while (true) {
      System.out.println(new Date().getTime() / 1000);
      try {
        Thread.sleep(1000);
      }
      catch (InterruptedException ignore) {
      }
    }
  }
}
HTML
<HTML><body>
<script language="javascript">
if(document.all){
  setInterval(function(){
  time=parseInt(new Date().getTime()/1000);
  document.body.innerText=time;
  },1000);
} else{
  setInterval(function(){
  time=parseInt(new Date().getTime()/1000);
  document.body.textContent=time;
  },1000);
}
</script></body></HTML>

Can you think of any more?

Happy 1234567890 day!

Monday, January 12, 2009

Set Google's new icon in Firefox's Search Box

This is how you can update your search box in Firefox to display Google's brand new favicon:

1. Go to the searchplugins directory, where you installed Firefox. In Windows XP, this will normally be: C:\program files\Mozilla Firefox\searchplugins

2. Open google.xml in your favourite text editor.

3. Change the Image tag to the following and save:

<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7PT7/3zF6/9Ptu//RbHx/0227/+Tzvb/9vv5/97h0f9JeBz/NHoA/z98Av9AfAD/PHsA/0F6AP8AAAAA/vz7/1+33/8Mp+z/FrHw/xWy8f8bs/T/Hqrx/3zE7v////7/t8qp/zF2A/87gwH/P4ID/z59AP8+egD/Q3kA/97s8v8botj/ELn3/wy58f8PtfL/D7Lw/xuz9P8vq+f/8/n///779v9KhR3/OYYA/0GFAv88hgD/QIAC/z17AP/0+/j/N6bM/wC07/8Cxf7/CsP7/wm+9v8Aqur/SrDb//7+/v///P7/VZEl/zSJAP87jQD/PYYA/0OBBf8+fQH///3//9Dp8/84sM7/CrDf/wC14/8CruL/KqnW/9ns8f/8/v//4OjX/z+GDf85kAD/PIwD/z2JAv8+hQD/PoEA/9C7pv/97uv////+/9Xw+v+w3ej/ls/e/+rz9///////+/z6/22mSf8qjQH/OJMA/zuQAP85iwL/PIgA/zyFAP+OSSL/nV44/7J+Vv/AkG7/7trP//7//f/9//7/6/Lr/2uoRv8tjQH/PJYA/zuTAP87kwD/PY8A/z2KAP89hAD/olkn/6RVHP+eSgj/mEgR//Ho3//+/v7/5Ozh/1GaJv8tlAD/OZcC/zuXAv84lAD/O5IC/z2PAf89iwL/OIkA/6hWFf+cTxD/pm9C/76ihP/8/v//+////8nav/8fdwL/NZsA/zeZAP83mgD/PJQB/zyUAf84jwD/PYsB/z6HAf+fXif/1r6s//79///58u//3r+g/+3i2v/+//3/mbiF/yyCAP87mgP/OpgD/zeWAP85lgD/OpEB/z+TAP9ChwH/7eHb/////v/28ej/tWwo/7tUAP+5XQ7/5M+5/////v+bsZn/IHAd/zeVAP89lgP/O5MA/zaJCf8tZTr/DyuK//3////9////0qmC/7lTAP/KZAT/vVgC/8iQWf/+//3///j//ygpx/8GGcL/ESax/xEgtv8FEMz/AALh/wAB1f///f7///z//758O//GXQL/yGYC/8RaAv/Ojlf/+/////////9QU93/BAD0/wAB//8DAP3/AAHz/wAA5f8DAtr///////v7+/+2bCT/yGMA/89mAP/BWQD/0q+D///+/////P7/Rkbg/wEA+f8AA/z/AQH5/wMA8P8AAev/AADf///7/P////7/uINQ/7lXAP/MYwL/vGIO//Lm3P/8/v//1dT2/woM5/8AAP3/AwH+/wAB/f8AAfb/BADs/wAC4P8AAAAA//z7/+LbzP+mXyD/oUwE/9Gshv/8//3/7/H5/zo/w/8AAdX/AgL6/wAA/f8CAP3/AAH2/wAA7v8AAAAAgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAA%3D%3D</Image>
4. Restart Firefox to see the new icon in your search box!

Here is a useful page you can use to convert files into base64 encoded strings: http://www.greywyvern.com/code/php/binary2base64

Related Posts:
How-to write your own Search Plugin for Firefox

Friday, January 02, 2009

fahd.blog in 2008

Happy 2009!
I'd like to wish everyone a great start to an even greater new year!

During 2008, I posted 44 new entries on fahd.blog, which is 52% more than in 2007. In addition to more posts, I am thrilled that I have more readers from all over the world too! Thanks for reading!

Top 5 posts of 2008:

I'm going to be writing a lot more this year, so stay tuned for more great techie tips, tricks and hacks! :)