| 0 comments ]

My IzPack installation script that I use for my installer has changed tremendously to integrate easily with our production system which needed installation environment properties defined in a properties file instead of being supplied by the customer. With this system, the client defines all the variables in the properties file and supply the packed zip file at installation time. The Zip file is created by us from a Technical questionnaire(TQ) given to the client at the project initiation phase. This way, we control all the parameters and customer does not make any input during the installation. We also have a Java Service wrapper file to install it automatically as a service. The file is parsed by the IzPack script to insert all the variables. We currently use this to integrate into more than ten different LLPG and NLPG systems.

CHANGE THE PATHS TO SUIT YOUR ENVIRONMENT

So here I will post my scripts: build.xml

Ant Integration Task

<target name="build-dev-installer" depends="deploy_zip,create-dev-installer">
  </target>
  
  <target name="create-dev-installer" description="Create Windows Installer">

    <echo message=" "/>
    <echo message="Setting property and task for installer creation" />

    <taskdef name="launch4j"
        classname="net.sf.launch4j.ant.Launch4jTask"
        classpath="${launch4j.dir}/launch4j.jar:${launch4j.dir}/lib/xstream.jar"/>

    <echo message=" "/>
    <echo message="Preparing ...." />

    <delete dir="${packaging.dir}"/>
    <mkdir dir="${packaging.dir}" />

    <mkdir dir="${packaging.build.dir}" />

    <copy todir="${deploy.dir}" file="${catalina.source}" failonerror="true"/>

    <copy todir="${packaging.dir}">
      <fileset dir="installer">
        <include name="nizpack.xml"/>
        <include name="launch4jConfig.xml"/>
        <include name="launcher.ini"/>
      </fileset>
      <fileset dir="src/web">
        <include name="lalpac.ico"/>
        <include name="lal_Logo3d.gif"/>
      </fileset>
    </copy>

    <replace dir="${packaging.dir}">
      <include name="*.xml"/>
      <replacefilter token="[APP-NAME]" value="${app.name}" />
      <replacefilter token="[BUILD-VERSION]" value="${build.version}" />
      <replacefilter token="[APP-VERSION]" value="${app.version}" />
      <replacefilter token="[APP-PATH]" value="${app.subpath}" />
      <replacefilter token="[JAVA-VERSION]" value="${java.version.minimum}" />
      <replacefilter token="[APP-URL]" value="${app.url}" />
      <replacefilter token="[APP-AUTHOR]" value="${app.author}" />
      <replacefilter token="[APP-CONTACT]" value="${app.contact}" />
      <replacefilter token="[CATALINA-VERSION]" value="${catalina.version}" />
      <replacefilter token="[CATALINA-SOURCE]" value="${catalina.source}" />
      <replacefilter token="[FILE-NAME]" value="${izpack-installer}" />
      <replacefilter token="[DEPLOY]" value="${deploy.win}" />
      <replacefilter token="[PACKAGING_BUILD]" value="${packaging.build.dir}" />
      <replacefilter token="[COPY_RIGHT]" value="${app.copyright}" />
      <replacefilter token="[ANT_HOME]" value="${ant.dir}" />
      <replacefilter token="[PACK_SOURCE]" value="${deploy.dir}" />
      <replacefilter token="[PKG_DEV]" value="${deploy.dir}" />
    </replace>

    <echo message=" "/>
    <echo message="Makes the installer using IzPack to ${izpack-installer}"/>
    <izpack input="${basedir}/${packaging.dir}/nizpack.xml"
              output="${deploy.dir}/${izpack-installer}"
              installerType="standard"
              basedir="${basedir}"
              izPackDir="${izpack.dir}/"/>


    <echo message=" Creating Launch4j Pack"/>

    <mkdir dir="${ms.install.dev}/${ms.package.name}" />

    <launch4j configFile="${packaging.dir}/launch4jConfig.xml"/>

    <echo message=" "/>
    <echo message="cleaning and finalizing release" />
    <delete dir="${packaging.dir}"/>

  </target>



The IzPack Script: nizpack.zml

<installation version="1.0">
  <info>
    <appname>[APP-NAME]</appname>
    <appversion>[APP-VERSION]</appversion>
    <appsubpath>[APP-PATH]</appsubpath>
    <javaversion>[JAVA-VERSION]</javaversion>
    <summarylogfilepath>$INSTALL_PATH/installinfo/Summary.htm</summarylogfilepath>
    <url>[APP-URL]</url>

    <authors>
      <author name="[APP-AUTHOR]" email="[APP-CONTACT]"/>
    </authors>
  </info>
  <guiprefs width="700" height="530" resizable="yes">
    <modifier key="useLabelIcons" value="no"/>
    <modifier key="useHeadingPanel" value="yes"/>
    <modifier key="headingLineCount" value="1"/>
    <modifier key="headingFontSize" value="2"/>
    <modifier key="useFlags" value="no"/>
    <modifier key="langDisplayType" value="native"/>
    <modifier key="allYGap" value="8"/>
    <modifier key="allXGap" value="4"/>
    <modifier key="labelGap" value="2"/>

    <modifier key="headingPanelCounter" value="progressbar"/>
    <modifier key="headingPanelCounterPos" value="inNavigationPanel"/>
  </guiprefs>
  <locale>
    <langpack iso3="eng"/>
  </locale>
  <native name="ShellLink.dll" type="izpack">
    <os family="windows"/>
  </native>
  <native name="COIOSHelper.dll" stage="both" type="3rdparty">
    <os family="windows"/>
  </native>
  <listeners>
    <listener installer="RegistryInstallerListener" uninstaller="RegistryUninstallerListener">
      <os family="windows"/>
    </listener>
    <listener installer="AntActionInstallerListener" uninstaller="AntActionUninstallerListener"/>
  </listeners>

  <!-- Conditions -->
  <conditions>
    <condition type="variable" id="condition.qas.gbr">
      <name>connector.type</name>
      <value>QAS_GBR_V6</value>
    </condition>
    <condition type="variable" id="condition.qas.lpg">
      <name>connector.type</name>
      <value>QAS_LPG_V6</value>
    </condition>




    <condition type="variable" id="start.addresshub">
      <name>start_addresshub</name>
      <value>true</value>
    </condition>
    <condition type="variable" id="condition.mvm">
      <name>connector.type</name>
      <value>MVM</value>
    </condition>
    <condition type="variable" id="condition.sql">
      <name>connector.type</name>
      <value>SQL</value>
    </condition>
  </conditions>

  <!-- Variables -->
  <dynamicvariables>
    <variable name="TOMCAT_HOME" value="$INSTALL_PATH\$TOMCAT_VER"/>
    <variable name="JDK_HOME" value="${jdk.home}"/>
    <variable name="TEMP_HOME" value="$INSTALL_PATH\temp"/>
    <variable name="JDKPathPanel.minVersion" value="1.5"/>
    <variable name="JDKPathPanel.maxVersion" value="1.5"/>
    <variable name="JDKPathPanel.skipIfValid" value="yes"/>
    <variable name="ADDRESSHUB_CONF_FILE" value="${addresshub.file}"/>

  </dynamicvariables>
  <variables>
    <variable name="TOMCAT_VER" value="[CATALINA-VERSION]"/>

  </variables>
  <resources>
    <res src="installer/ProcessPanel.Spec.xml" id="ProcessPanel.Spec.xml"/>
    <!-- res src="installer/shortcutSpec.xml" id="shortcutSpec.xml"/ -->
    <res src="installer/UserInput.Spec.xml" id="userInputSpec.xml" />
    <res src="installer/antActionsSpec.xml" id="AntActionsSpec.xml" />
    <res id="Heading.image" src="src/web/lalpac-logo-cms.png"/>
  </resources>
  <panels>
    <panel classname="HelloPanel"/>
    <panel classname="TargetPanel"/>
    <panel classname="UserInputPanel" id="Connector.Select"/>
    <panel classname="InstallPanel"/>


    <!-- panel classname="ShortcutPanel"/ -->
    <panel classname="SimpleFinishPanel"/>
  </panels>
  <packs>
    <pack name="Installing Selected Packages" required="yes">
      <description>LalPac Addresshub core installation.</description>
      <file targetdir="$INSTALL_PATH" src="[PACK_SOURCE]/[CATALINA-VERSION].zip" unpack="true"/>
      <file targetdir="$INSTALL_PATH/[CATALINA-VERSION]" src="[PACK_SOURCE]/lalpac-tomcat-customised.zip" unpack="true"/>
      <file targetdir="$INSTALL_PATH/[CATALINA-VERSION]" src="[PACK_SOURCE]/lalpac-tomcat-nt-service.zip" unpack="true"/>
      <singlefile target="$INSTALL_PATH/temp/installer_scripts.xml" src="installer/installer_scripts.xml"/>
      <parsable type="plain" parse="yes" targetfile="$INSTALL_PATH/[CATALINA-VERSION]/conf/wrapper.conf"/>
      <parsable type="at" targetfile="$INSTALL_PATH/temp/installer_scripts.xml" parse="yes"/>
      <file targetdir="$INSTALL_PATH/temp" src="[PACK_SOURCE]/lalpac-tomcat.zip" unpack="true"/>
      <singlefile target="$INSTALL_PATH/temp/web.xml" src="src/conf/dist.web_1.xml"/>
      <singlefile target="$INSTALL_PATH/temp/server.xml" src="src/conf/dist.server.win.xml"/>
      <singlefile target="$INSTALL_PATH/temp/qas/QAWSERVE_GBR.INI" src="src/conf/qas/dist.QAWSERVE_GBR.INI"/>
      <singlefile target="$INSTALL_PATH/temp/qas/QAWSERVE_LPG.INI" src="src/conf/qas/dist.QAWSERVE_LPG.INI"/>
      <singlefile target="$INSTALL_PATH/temp/qas/QAWORLD.INI" src="src/conf/qas/QAWORLD_dist.INI"/>
      <parsable type="plain" parse="yes" targetfile="$INSTALL_PATH/temp/qas/QAWORLD.INI"/>
    </pack>
  </packs>

  <jar src="[ANT_HOME]/lib/ant.jar" stage="both"/>
  <jar src="[ANT_HOME]/lib/ant-launcher.jar" stage="both"/>

</installation>



The Packing Time Ant Integration Script which invokes the ant script: AntActionsSpec.xml
<?xml version="1.0" encoding="UTF-8"?>
<antactions>
 <pack name="Installing Selected Packages">
  <antcall order="afterpacks" buildfile="$TEMP_HOME/installer_scripts.xml" logfile="$TEMP_HOME/ant-log.txt" verbose="yes">
   <property name="extract_from" value="$ADDRESSHUB_CONF_FILE"/>
   <property name="extract_to" value="$TEMP_HOME"/>
   <target name="extract"/>
  </antcall>

  <antcall order="afterpacks" buildfile="$TEMP_HOME/installer_scripts.xml" logfile="$TEMP_HOME/ant-log.txt" verbose="yes">
   <property name="extract_to" value="$TEMP_HOME"/>
   <property name="tomcat.home" value="$TOMCAT_HOME"/>
   <property name="install.path" value="$INSTALL_PATH"/>
   <target name="setup"/>
  </antcall>

  <antcall order="afterpacks" buildfile="$TEMP_HOME/installer_scripts.xml" logfile="$TEMP_HOME/ant-log.txt" verbose="yes">
   <property name="extract_to" value="$TEMP_HOME"/>
   <property name="tomcat.home" value="$TOMCAT_HOME"/>
   <property name="install.path" value="$INSTALL_PATH"/>
   <target name="ConnectorConfig"/>
  </antcall>

  <antcall order="afterpacks" uninstall_order="beforedeletion" buildfile="$TEMP_HOME/installer_scripts.xml">
   <target name="InstallService"/>
   <uninstall_target name="UninstallService"/>
  </antcall>
 </pack>
</antactions>


The actual Ant script that is executed: installer_scripts.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="InstallerScripts" basedir=".">

  <!-- This target extracts the Config pack received at install time-->
  <target name="extract" description="This target extracts the Config pack received at install time">
      <!-- TODO define script. See Help menu or http://ant.apache.org/ -->
    <unzip src="${extract_from}" dest="${extract_to}"/>
  </target>

  <!-- This target sets up the core addresshub without any specific Connector Configurations-->
  <target name="setup" description="This target sets up the core addresshub without any specific Connector Configurations">
    <!-- Set The properties-->
    <property file="${extract_to}/install.properties"/>

    <!-- Replace the placeholders-->
    <replace dir="${install.path}/temp">
      <include name="*.xml"/>
      <replacefilter token="[START-PORT]" value="${port.startup}" />
      <replacefilter token="[SHUTDOWN-PORT]" value="${port.shutdown}" />
      <replacefilter token="[LOCALHOST]" value="${server.name}" />
    </replace>

    <!-- Replace the webapps folder-->
    <delete dir="@TOMCAT_HOME/webapps" failonerror="true">
    </delete>
    <echo message="webapps deleted" />
    <mkdir dir="@TOMCAT_HOME/webapps"/>
    <copy todir="@TOMCAT_HOME/webapps" failonerror="true" verbose="true">
      <fileset dir="@TEMP_HOME/webapps" >
      </fileset>
    </copy>

    <!-- Replace the Addresshub properties -->
    <delete file="@TOMCAT_HOME/webapps/lalpac-addresshub/WEB-INF/classes/addresshub.properties"/>
    <copy file="${extract_to}/addresshub.properties" tofile="${tomcat.home}\webapps\lalpac-addresshub\WEB-INF\classes\addresshub.properties"  verbose="true"  failonerror="true"  />

    <!-- Replace the Web.xml properties -->
    <delete file="@TOMCAT_HOME/webapps/lalpac-addresshub/WEB-INF/web.xml"/>
    <copy file="@TEMP_HOME/web.xml"  tofile="${tomcat.home}\webapps\lalpac-addresshub\WEB-INF\web.xml"  verbose="true"  failonerror="true"  />

    <!-- Replace the Tomcat Configurations  -->
    <copy file="@TEMP_HOME/server.xml" tofile="@TOMCAT_HOME/conf/server.xml" failonerror="true" />
    <!-- copy file="@TEMP_HOME/conf/catalina.policy" tofile="@TOMCAT_HOME/conf/catalina.policy"  failonerror="true" / -->
    <!-- copy file="@TEMP_HOME/conf/wrapper.conf" tofile="@TOMCAT_HOME/conf/wrapper.conf" failonerror="true" / -->
  </target>

  <target name="ConnectorConfig">
    <property file="${extract_to}/install.properties"/>
    <antcall target="qas.conf"/>
  </target>

    <condition property="qas.lpg.installed">
        <equals arg1="${install.type}" arg2="qas_lpg" casesensitive="false"/>
    </condition>

  <target name="qas.conf"  if="qas.lpg.installed">
      <copy todir="@TOMCAT_HOME\shared\lib" >
        <fileset dir="//${qas.api.home}" >
          <include name="qalcl.dat"/>
          <include name="qalicn.ini"/>
          <include name="qaupied.dll"/>
          <include name="qaupied.044"/>
        </fileset>
      </copy>

      <copy file="@TEMP_HOME/qas/QAWSERVE_LPG.INI" tofile="@TOMCAT_HOME/shared/lib/QAWSERVE.INI" failonerror="true"/>
      <copy file="@TEMP_HOME/qas/QAWORLD.INI" tofile="@TOMCAT_HOME/shared/lib/QAWORLD.INI" failonerror="true"/>

      <replace file="@TOMCAT_HOME\shared\lib\QAWSERVE.INI" propertyFile="${extract_to}\install.properties">
        <replacefilter token="@QAS_DATA_DIR@" property="qas.data.home"/>
      </replace>      
  </target>

  <target name="InstallService">
    <exec executable="@TOMCAT_HOME\bin\Tomcat-NT-service-install.bat">
    </exec>
  </target>

  <target name="UninstallService">
    <exec executable="@TOMCAT_HOME\bin\Tomcat-NT-service-uninstall.bat">
    </exec>
  </target>

</project>



Lastly, the Lauch4j script

The Lunch4jt script that is executed: launch4jConfig.xml
<launch4jConfig>
    <!-- Header types: 0-GUI, 1-console -->
    <headerType>0</headerType>
    <outfile>[PKG_DEV]/[FILE-NAME].exe</outfile>
    <jar>[PACK_SOURCE]/[FILE-NAME].jar</jar>
    <dontWrapJar>false</dontWrapJar>
    <errTitle>[APP-NAME]: Installation Error</errTitle>
    <customProcName>LalPacAddresshub</customProcName>
    <downloadUrl>https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=jre-1.5.0_15-oth-JPR@CDS-CDS_Developer</downloadUrl>
    <cmdLine/>
    <chdir/>
    <icon>lalpac.ico</icon>
    <stayAlive/>
    <jre>
        <minVersion>1.5.0</minVersion>
        <maxVersion>1.5.0_15</maxVersion>
        <initialHeapSize>0</initialHeapSize>
        <maxHeapSize>0</maxHeapSize>
    </jre>
  </launch4jConfig>


| 0 comments ]

From this blog post by the main guy behind IzPack regarding the use of IzPack in both Sun Grid Engine and Terracotta, I went on a look out to find the code so I could borrow some code examples and thanks to the world of open source, I found them so here, you go.

 IzPack Install example form Sun Grid System Installation
Click Here to view

IzPack Code as used in Terracotta
Click Here to View


Enjoy

| 0 comments ]

This is my own implementation of the Money value object from Martin Fowler's book Partterns of Enterprise Application Architecture. Enjoy

package com.console.utils.value;

import com.console.core.exceptions.UnknownCurrencyCodeException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Currency;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Assert;
import static java.math.RoundingMode.HALF_UP;

/**
 *
 * @author farouka
 */
public class Money implements Serializable {

  /**
   * Why me
   */
  private static final int[] cents = new int[]{1, 10, 100, 1000};
  
  private BigDecimal amount;
  
  private Currency currency;
  
  private MathContext DEFAULT_CONTEXT = new MathContext( 2, HALF_UP );

  public Money(long amount, Currency currency) {
    this.currency = currency;
    this.amount = BigDecimal.valueOf(amount, currency.getDefaultFractionDigits()); 
  }

  /**
   * Creates a currency object from the long value provided assuming the long value
   * represents the base currency in the least monetary unit. For eg, new Money(500, "GBP")
   * is assumed to mean 5.00 great british pounds
   * @param amount in base monetary unit
   * @param currCode
   * @throws com.console.core.exceptions.UnknownCurrencyCodeException
   */
  public Money(long amount, String currCode) throws UnknownCurrencyCodeException {
    this( amount, Currency.getInstance(currCode) );
  }
  
  /**
   * Construct an IMMUTABLE money object from a double. It is assumed that 
   * the whole part of the double is the Money with the fractional part representing
   * lowest denominator of the currency. For eg, new Money (50.99, "GBP") is assumed
   * to be 50 pounds and 99 pence.
   * PS. 89.788 will be truncated to 89.78 based on the defaultcurrencydigit of the currency
   * @param amount
   * @param curr
   */
  public Money(double amount, Currency curr) {
    this.currency = curr;
    BigDecimal bd = BigDecimal.valueOf( amount );
    this.amount = bd.setScale(centFactor(), HALF_UP);
  }

  private Money() {
  }

  /**
   * Construct an IMMUTABLE money object from a double. It is assumed that 
   * the whole part of the double is the Money with the fractional part representing
   * lowest denominator of the currency. For eg, new Money (50.99, "GBP") is assumed
   * to be 50 pounds and 99 pence.
   * PS. 89.788 will be truncated to 89.78 based on the defaultcurrencydigit of the currency
   * code supplied
   * @param amount
   * @param currCode iso 4217 currency code
   * @throws com.console.core.exceptions.UnknownCurrencyCodeException
   */
  public Money(double amount, String currCode) throws UnknownCurrencyCodeException {
    this.currency = Currency.getInstance(currCode);
    BigDecimal bd = BigDecimal.valueOf( amount );
    this.amount = bd.setScale( currency.getDefaultFractionDigits(), HALF_UP);
  }
  
  /**
   * Constructs an IMMUTABLE money from a BigDecimal. the BigDecimal provided is only scaled
   * to used the default digits in currency object represented by the sting parameter
   * @param bigDecimal
   * @param currCode ISO 4217 cuurency code
   * @throws com.console.core.exceptions.UnknownCurrencyCodeException
   */
  public Money(BigDecimal bigDecimal, String currCode ) throws UnknownCurrencyCodeException {    
    this.currency = Currency.getInstance(currCode);
    this.amount = bigDecimal.setScale( currency.getDefaultFractionDigits(), HALF_UP);
  }

  /**
   * Constructs an IMMUTABLE money from a BigDecimal. the BigDecimal provided is only scaled
   * to used the default digits in currency object represented by the sting parameter
   * @param multiply
   * @param currency
   */
  public Money(BigDecimal bigDecimal, Currency currency) {
    this.currency = currency;   
    this.amount = bigDecimal.setScale( currency.getDefaultFractionDigits(), HALF_UP);
  }

//  public  boolean assertSameCurrencyAs(Money arg) {
//    return  this.currency.getCurrencyCode().equals(arg.currency.getCurrencyCode());
//  }
//  
  public boolean assertSameCurrencyAs(Money money) throws IncompatibleCurrencyException{
  if ( this.currency == null ) {
   throw new IncompatibleCurrencyException( "currency.invalid" );
  }
  if ( money == null ) {
   throw new IncompatibleCurrencyException( "currency.invalid" );
  }
    Assert.assertEquals("money math mismatch", currency, money.currency);
    return true;
  }

  private int centFactor() {
    return cents[ getCurrency().getDefaultFractionDigits() ];
  }

  public BigDecimal amount() {
    return amount;
  }
  
  public long amountAsLong(){
    return amount.unscaledValue().longValue();
  }

  public Currency getCurrency() {
    return currency;
  }
//    common currencies
  public static Money dollars(double amount) {
    Money result = null;
    try {
      result = new Money(amount, "USD");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }
  
  public static Money dollars(long amount) {
    Money result = null;
    try {
      result = new Money(amount, "USD");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }

  public static Money pounds(double amount) {
    Money result = null;
    try {
      result = new Money(amount, "GBP");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }
  
  public static Money pounds(long amount) {
    Money result = null;
    try {
      result = new Money(amount, "GBP");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }
  
  public static Money pounds(BigDecimal amount) {
    Money result = null;
    try {
      result = new Money(amount, "GBP");
    } catch (UnknownCurrencyCodeException ex) {
      Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }


  @Override
  public int hashCode() {
    int hash = (int) ( amount.hashCode() ^ (amount.hashCode() >>> 32) );
    return hash;
  }

  @Override
  public boolean equals(Object other) {
    return (other instanceof Money && equals((Money) other));
  }

  public boolean equals(Money other) {
    return ( currency.equals(other.currency) && (amount.equals(other.amount)) );
  }

  public Money add(Money other) throws Exception{
    assertSameCurrencyAs( other );
    return newMoney(amount.add(other.amount, DEFAULT_CONTEXT));
  }

  private int compareTo(Money money) throws Exception {
    assertSameCurrencyAs( money );
    return amount.compareTo( money.amount ); 
  }

  public Money multiply(BigDecimal amount) {
    return new Money( this.amount().multiply(amount, DEFAULT_CONTEXT), currency);
  }

  public Money multiply( BigDecimal amount, RoundingMode roundingMode ) {
    MathContext ct = new MathContext( currency.getDefaultFractionDigits(), roundingMode );
    return new Money( amount().multiply(amount, ct), currency);
  }

  private Money newMoney(BigDecimal amount) {
    return new Money( amount, this.currency );
  }
  
  public Money multiply(double amount) {
    return multiply( new BigDecimal( amount ) );
  }

  public Money subtract(Money other) throws Exception {
    assertSameCurrencyAs(other);
    return newMoney( amount.subtract(other.amount, DEFAULT_CONTEXT) );
  }
  
  public int compareTo(Object other) throws Exception {
    return compareTo((Money) other);
  }

  public boolean greaterThan(Money other)throws Exception {
    return (compareTo(other) > 0);
  }
  
//  public Money[] allocate(int n){
//    Money lowResult = newMoney( amount.unscaledValue().longValue()/n );
//    Money highResult = newMoney(lowResult.amount + 1);
//    Money[] results = new Money[n];  
//    int remainder = (int) amount % n;
//    
//    for(int i = 0; i < remainder; i++)results[i] = highResult;
//    for(int i = 0; i < n; i++) results[i] = lowResult;
//    
//    return results;
//  }
//  
//  public Money[]allocate(long[] ratios){
//    long total = 0;
//    for (int i = 0; i < ratios.length; i++) {
//      total += ratios[i];
//    }
//    long remainder = amount;
//    Money[] results = new Money[ratios.length];
//    for (int i = 0; i < results.length; i++) {
//      results[i] = newMoney(amount * ratios[i]/total);
//      remainder -= results[i].amount;
//    }
//    for (int i = 0; i < remainder; i++) {
//      results[i].amount++;
//    }
//    return results;
//
//  }
  
  public Money divideByNumber( double divisor){
     BigDecimal div = BigDecimal.valueOf( divisor );
     BigDecimal ans = this.amount.divide(div, DEFAULT_CONTEXT);
     return new Money(ans, this.currency);
  }
  
  public int getQuotient( Money divisor ){
    BigDecimal ans = this.amount.divide(divisor.amount, RoundingMode.DOWN);
    return ans.intValue();
  }
  
  /**
   * divides toe moneys and return the quotient and Remainder this method has been customised,
   * for my money transfer needs...sorry
   * @param divisor
   * @return
   */
  public int[] getQuotientandRemainder(Money divisor){
    int[] ans = new int[2];
    BigDecimal[] bdArr = this.amount.divideAndRemainder(divisor.amount, DEFAULT_CONTEXT);
    BigDecimal quo = bdArr[0];
    BigDecimal rem = bdArr[1];
    ans[0] = quo.intValue();
    if( rem.compareTo(BigDecimal.ZERO) == 0 ){
      ans[1] =0;
    }else{
      ans[1] = 1;
    }
    return ans;
  }
  
 public String toFormattedString() {
  NumberFormat nf = NumberFormat.getCurrencyInstance();
  nf.setCurrency( currency );
  nf.setGroupingUsed( true );
  nf.setMaximumFractionDigits( currency.getDefaultFractionDigits() );
  return nf.format( this.amount.doubleValue() );
 }
  
 /**
  * Returns the ISO-4217 currency code of the currency
  * attached to this money.
  * 
  * @return The ISO-4217 currency code.
  */
 public String getCurrencyCode() {
  return currency.getCurrencyCode();
 }
  
  @Override
  public String toString() {
      return amount.toString();
  }
  
 /**
  * Returns the precision for this money. The precision is the total number
  * of digits that the value can represent. This includes the integer part.
  * So, 18 would be able to represent:
  * 

* 1234567890.12345678
  * 

* 1234567890123456.78
  * 

* 123456789012345678
  * 

* 0.123456789012345678
  * 
  * @return The precision. 
  */ 
 public int precision() {
  return amount.precision();
 }

 /**
  * Returns the 'scale' for this money. The scale is the number of 
  * digits that are moved to the fractional part, assuming that all
  * digits are represented by a single integer value. For example:
  * 

* If: 123456789012345678 has scaling 2, it would be :
  * 

* 1234567890123456.78
  * 
  * @return The scale value. 
  */
 public int scale() {
  return amount.scale();
 }

 /**
  * Returns the sign for the money (negative or positive).
  * -1 if negative, 0 if 0.00 (zero), 1 if positive.
  * 
  * @return The sign of the money. 
  */ 
 public int signum() {
  return amount.signum();
 }
}


And here is the UnknownCurrencyCodeException class
package com.console.lib.utils;

/**
 * An exception which is raised when an unrecognised currency code is passed to the
 * Currency class.
 *
 * @author Farouk Alhassan
 * @see Currency
 */
public class UnknownCurrencyCodeException extends Exception {

    // Reason for exception
    private String reason = null;

    /**
     * Create a new unknown currency code exception.
     *
     * @param reason for the exception
     */
    public UnknownCurrencyCodeException(String reason) {
        this.reason = reason;
    }

    /**
     * Return the reason this exception was raised.
     *
     * @return the reason why the string isn't a valid currency code
     */
    public String getReason() {
        return reason;
    }

    /**
     * Convert the exception to a string
     *
     * @return string version of the exception
     */
    public String toString() {
 return getReason();
    }
}