- Right-click on the desktop and choose
New > Shortcut
. - Type
shutdown.exe -s -t 00
in the location box and pressNext
. - Type
Turn off computer
as the name of the shortcut and then pressFinish
. - Right-click the new icon that has appeared on the desktop and select
Properties
. - Click the
Change Icon...
button on theShortcut
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.
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:
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.
Labels:
Java,
programming,
swing
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
.
- On the menubar go to
Actions > New > New session...
to open thePreferences
dialog. - Click on the
Providers
tab. - Right-click to
Add Group
and call it "weblogic92", for example. - Right-click on
Library
toAdd 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.
- On the menubar go to
Actions > New > New session...
to open thePreferences
dialog. - Click on the
Sessions
tab. - Type a name for the session. For example, "myweblogic".
- In the
Plug In
list selectBEA WebLogic
. - Under
Connection Factory
class, pickhermes.JNDITopicConnectionFactory
. - Select
weblogic92
(defined in step 2) from theLoader
dropdown. - Add property:
initialContextFactory
with a value ofweblogic.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 selectingRemove
. - Press
OK
.
- 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.
Labels:
hermes,
Java,
JMS,
programming,
weblogic
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(); } }
Labels:
Java,
programming
Wednesday, October 21, 2009
Bash Globbing
Globbing refers to the expansion of shell metacharacters to complete file names. For example, when you run
By default, Bash does not glob dot-files. This means that
There is also an option to turn on case-insensitive globbing:
ls *
, the shell expands the wildcard * into a list of files and passes them as arguments to ls
.
sharfah@starship:~> ls * file1 File2Dot 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 File2Case-Insensitive Globbing
There is also an option to turn on case-insensitive globbing:
sharfah@starship:~> shopt -s nocaseglob sharfah@starship:~> ls f* file1 File2To 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.
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=y2. 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
The backup appender will be used if the primary appender fails. My backup is a
My primary appender is a
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.)
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>
Labels:
Java,
log4j,
logging,
programming
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
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.
Now add the code you wish to highlight in your webpage, surrounded by the
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)
shBrushBash.js
as it wasn't formatting file redirect characters (>, <) correctly.
This is how you can upgrade too:
1. Download SyntaxHighlighter v2.0You 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)
Labels:
html,
Java,
programming,
syntaxhighlighter,
webdesign
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 thekill
command. If it has not died within 10 seconds, it executes the harderkill -9
command.restart
: Restarts the application by callingstop
and thenstart
.status
:Reads the pid file and invokeskill -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 invokingkill -3
on the pidpurge
: Purges log files
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 LASTTo 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");
Labels:
Java,
programming
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.jmxremoteLet me know if there are any other improvements I can make!
Labels:
eclipse,
performance
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
Percent Sign in Crontab
/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 2009We 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.
In this approach, we hide the retry counter in a separate class called
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
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:
Labels:
Java
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:
Tortoise FAQs
CollabNet Help
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)
- 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
- 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/moduleReferences:
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:
In Firefox 3.5 (it's easier):
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:
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)
- 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...
keytool -printcert -file mycert.cer2) 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 mytruststoreYou can also display the contents of the keystore using the following command:
keytool -list -v -keystore mytruststore
Labels:
certificate,
Java,
keytool,
SSL
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.
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
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
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.
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.
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
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.txtThis 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.txtIn 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.
xargsThis 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.txtThis 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.42sI'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.
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
In order to find out how much physical memory is installed, use
To show machine, software revision and patch revision information use the
In order to list the processes running, use
Linux - CPU, Memory and Version
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 MegabytesVersion 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 i86pcProcesses:
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/30Related 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
You need to spool in order to write the output of your sql commands to a file. Turn it off when you are done.
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.
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.
You can specify the size of individual columns like this:
Use TTITLE to display a heading before you run a query.
The shell script below uses SQL*Plus to create a report.
Use the
-s
flag on your sqlplus command in order to inhibit output such as the SQL*Plus banner and prompt.
Spooling to a fileYou 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 offPage 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 50000Line 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 onColumn Size
You can specify the size of individual columns like this:
SQL> col employee_name format a40Titles
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 ---------- 1Use 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 ---------- 1Example 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:
Run the following command:
Quick Maven Commands
Skip Tests in Maven
Run the following command:
mvn release:prepareThis 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=OKPerform the release
Run the following command:
mvn release:performThis 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=deployRelated 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
Open the samba configuration file in your favourite editor, change security to user and add a username map.
The format is "unix username" = "samba username".
Make the following changes to smb.conf in order to share your home directory over samba and make it writable.
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 apt-get install samba smbfsEdit 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.confCreate a samba user
# "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
sudo smbpasswd -a fahdAdd the new user to the smbusers file
The format is "unix username" = "samba username".
sudo vi /etc/samba/smbusersShare home directory
fahd = fahd
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.confConnecting to the samba
#======================= 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
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:
Here's the same data put into a line chart.
The chart parameters I will use are:
I'm going to use a Pie Chart to show the number of posts per year.
The chart parameters I will use are:
Google Chart API
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)
<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 ChartHere'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)
<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 ChartI'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)
<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
Labels:
"google chart api",
charts,
google,
graphs,
webdesign
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
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.
Reading from a named pipe is the same as reading from a normal file. You can
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:
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 doneReading 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 doneMultiple 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
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
Labels:
"vertical rush",
climb,
stairs
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.
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!
Labels:
"vertical rush" stairs climb
Tuesday, March 03, 2009
Howto: Delete Empty Directories [Unix]
Consider the following directory structure:
/tmp |-->bar.txt |-->dir1/ |-->dir2/ | |-->baz.txt |-->dir3/ |-->foo.txtThere 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 directoryYou 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 emptyNote 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 $YESTTo 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
If you need to create sub-modules within your project, you need to change the
If you have a jar file called
To create a dependency on
Use the following command:
To skip tests use the property
Two commands must be invoked, in the following order:
In order to create a new maven project called
MyProject
run the following command:
mvn archetype:create -DgroupId=fs.work -DartifactId=MyProjectThis 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.javaThe pom file looks like this:
Creating sub-modules4.0.0 fs.work MyProject jar 1.0-SNAPSHOT MyProject http://maven.apache.org junit junit 3.8.1 test
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=jarThis 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.javaThe pom file for
MyProjectModule1
contains a reference to the parent and looks like this:
Deploying a jar to the repositoryMyProject 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
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=jarThis will create
dir/to/maven/fs/work/myarchive/1.0/myarchive-1.0.jar
in the maven repository.
Creating a dependencyTo create a dependency on
myarchive.jar
, add the following dependency to your pom:
Generating Eclipse .project and .classpath filesfs.work myarchive 1.0
Use the following command:
mvn eclipse:eclipseSkipping tests
To skip tests use the property
maven.test.skip=true
.
mvn -Dmaven.test.skip=true installRelease a project
Two commands must be invoked, in the following order:
mvn release:prepare mvn release:performOther 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 aSimpleDateFormat("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 ajava.sql.Types.DATE
column in my Oracle database.
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
Labels:
Java,
programming,
RMI
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.
Labels:
Java,
programming,
serialization
Friday, February 13, 2009
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; donePerl
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:
How-to write your own Search Plugin for Firefox
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! :)
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! :)
Subscribe to:
Posts (Atom)