This is the contents of a typical ant build file I use for Java coding projects. It’s up here in case it can help anyone else, and so I can copy and paste it when working off-site.
The process is fairly simple, I have my working area in a ~/projects/myapp folder (where myapp is name of current project). I have lib files in ~/projects/java/lib/
I run the build in the myapp file, the ant file reads the build.properties file for some config info and dumps the build outputs to the $targetdir folder (~/projects/bin)
Firstly, the build.properties file
# options: linux, windows, mac
gwt.os = linux
#Directories
gwt.installation.dir = ~/google/gwt-windows-1.5.3
#note env is defined in build.xml file
gwt.dir=${env.GWT_HOME}
tomcat.dir=${env.TOMCAT_HOME}
hibernate.dir=${env.HIBERNATE_HOME}
postgresql.dir=${env.POSTGRES_HOME}
asm.dir=${env.ASM_HOME}
spring.dir=${env.SPRING_HOME}
loglib.dir=${env.LOGLIB_HOME}
junit.dir=${env.JUNIT_HOME}
#JAR files
servlet.api.jar=${tomcat.dir}/lib/servlet-api.jar
gwt.servlet.jar=${gwt.dir}/gwt-servlet.jar
hibernate3.jar=${hibernate.dir}/hibernate3.jar
postgresql.driver.jar=${postgresql.dir}/postgresql-8.3-604.jdbc4.jar
asm.jar=${asm.dir}/asm-3.1.jar
loglib.jar=${loglib.dir}/commons-logging-1.1.1.jar
junit.jar=${junit.dir}/junit-4.5.jar
#google web toolkit jars
gwt.user.jar = ${gwt.installation.dir}/gwt-user.jar
gwt.servlet.jar = ${gwt.installation.dir}/gwt-servlet.jar
gwt.dev.jar = ${gwt.installation.dir}/gwt-dev-${gwt.os}.jar
build.dir = ../build
classes.dir = ${build.dir}/classes
jar.dir = ${build.dir}/jar
gwt.output = ${build.dir}/www
sourcedir = ${basedir}/src
targetdir = ${basedir}/bin
includedir = c:/projects/java/lib
testdir = ${basedir}/test
resdir = ${basedir}/res
reportsdir = ${basedir}/reports
appdir = ${basedir}
wwwdir = ${appdir}/www/com.petermac.${app}
spring.conf.dir = ${basedir}/src/META-INF/spring
hibernate.conf.dir = ${basedir}/src/META-INF/hibernate
Now for the actual ant file. This is specific to a GWT application, but you can easily knock out the google bits and use the rest without any changes.
<?xml version="1.0" encoding="utf-8" ?>
<project name="ClaimsManager" default="compile" basedir=".">
<property name="TALK" value="false" />
<!-- Define the environment property variable -->
<property environment="env" />
<!-- Define where our properties are located -->
<property file="build.properties" />
<description>
ClaimsManager build file. This is used to package up your project as a jar,
if you want to distribute it. This isn't needed for normal operation.
</description>
<!-- set classpath -->
<path id="classpath">
<pathelement location="${sourcedir}"/>
<pathelement location="${servlet.api.jar}" />
<pathelement location="${gwt.servlet.jar}" />
<pathelement location="${asm.jar}" />
<pathelement location="${postgresql.driver.jar}" />
<pathelement location="${loglib.jar}" />
<fileset dir="${includedir}">
<include name="*.jar" />
</fileset>
<fileset dir="${spring.dir}">
<include name="*.jar" />
</fileset>
</path>
<path id="gwt.compile.class.path">
<pathelement location="${sourcedir}"/>
<pathelement location="${testdir}"/>
<pathelement path="${gwt.user.jar}"/>
<pathelement path="${gwt.dev.jar}"/>
<pathelement path="${wwwdir}/WEB-INF/classes/com/petermac/claimsmanager/server/service"/>
</path>
<path id="test.classpath">
<pathelement location="${junit.jar}" />
<pathelement location="${gwt.user.jar}"/>
<pathelement path="${targetdir}"/>
<fileset dir="${includedir}">
<include name="*.jar" />
</fileset>
<fileset dir="${spring.dir}">
<include name="*.jar" />
</fileset>
</path>
<target name="init" description="Initialize the build environment">
<fail unless="app" message="Run ant -Dapp=[applicationName]"/>
<!-- Create the time stamp -->
<tstamp/>
<echo message="Building on ${TODAY} at ${TSTAMP}" />
<!-- Create directory structures -->
<mkdir dir="${targetdir}"/>
<mkdir dir="${reportsdir}"/>
<mkdir dir="${reportsdir}/raw/"/>
<mkdir dir="${reportsdir}/html/"/>
</target>
<target name="compile" depends="clean, init, copy-support" description="Compile server side java code" >
<javac srcdir="${sourcedir}"
destdir="${wwwdir}/WEB-INF/classes"
debug="true"
debuglevel="lines,vars,source"
source="1.6"
excludes="**/client/*.java,**/*.xml">
<classpath refid="classpath" />
<compilerarg value="-Xlint"/>
</javac>
<!-- copy www class files to bin folder -->
<copy todir="${targetdir}" >
<fileset dir="${wwwdir}/WEB-INF/classes/com/petermac/claimsmanager/client" includes="*.class" />
</copy>
<copy todir="${targetdir}" >
<fileset dir="${wwwdir}/WEB-INF/classes/com/petermac/claimsmanager/server/service" includes="*.class" />
</copy>
</target>
<target name="gwt-compile" depends="init" description="Execute the GWT compiler on client-side code" >
<!-- Invoke the GWT compiler -->
<java classpathref="gwt.compile.class.path" classname="com.google.gwt.dev.GWTCompiler" fork="true" >
<arg value="-out"/>
<arg value="${gwt.output}"/>
<arg value="com.petermac.claimsmanager.${app}"/>
</java>
</target>
<target name="clean" depends="clean-compile-test" description="Cleans build system by removing directories created during build">
<delete dir="${targetdir}"/>
<mkdir dir="${targetdir}"/>
<delete dir="${reportsdir}"/>
<delete dir="${wwwdir}"/>
</target>
<target name="prepare-www-dir" depends="init" description="Copy files to ${wwwdir} directory">
<mkdir dir="${wwwdir}/WEB-INF/classes" />
<mkdir dir="${wwwdir}/WEB-INF/lib" />
<!-- copy all public files to $wwwdir -->
<copy todir="${wwwdir}" >
<fileset dir="${sourcedir}/com/petermac/${app}/public" />
</copy>
<!-- copy the gwt servlet JAR to /WEB-INF/lib -->
<copy todir="${wwwdir}/WEB-INF/lib" file="${gwt.servlet.jar}" />
<!-- copy web.xml to /WEB-INF -->
<copy todir="${wwwdir}/WEB-INF/lib" file="${gwt.servlet.jar}" />
<!-- copy the web.xml deployment descritpor -->
<copy todir="${wwwdir}/WEB-INF/" file="${sourcedir}/web.xml" />
</target>
<!-- Copy relevant hibernate and spring files -->
<target name="copy-support" depends="prepare-www-dir" description="Copy hibernate/spring support files">
<copy todir="${wwwdir}/WEB-INF/lib" file="${postgresql.driver.jar}" />
<copy todir="${wwwdir}/WEB-INF/lib" file="${hibernate3.jar}" />
<!-- copy over the bulk of support jars -->
<copy todir="${wwwdir}/WEB-INF/lib" >
<fileset dir="${hibernate.dir}/" includes="*.jar" />
</copy>
<copy todir="${wwwdir}/WEB-INF/lib" >
<fileset dir="${spring.dir}/" includes="*.jar" />
</copy>
<!-- copy the hibernate and spring config files -->
<copy todir="${wwwdir}/WEB-INF/classes" >
<fileset dir="${spring.conf.dir}/" includes="*.xml" />
</copy>
<copy todir="${wwwdir}/WEB-INF/classes" >
<fileset dir="${hibernate.conf.dir}/" includes="*.xml" />
</copy>
</target>
<target name="deploy" depends="compile,gwt-compile" description="Deploy the application">
<war destfile="${app}.war" webxml="${wwwdir}/WEB-INF/web.xml">
<fileset dir="${wwwdir}"/>
<classes dir="${wwwdir}/WEB-INF/classes"/>
<lib dir="${wwwdir}/WEB-INF/lib" />
</war>
</target>
<!-- ////////////////////////// Test related targets /////////////////////////////////-->
<target name="clean-compile-test" description="Cleans the classes deployed used during unit tests">
<delete verbose="${TALK}">
<fileset dir="${testdir}" includes="**/*.class" />
</delete>
</target>
<target name="compile-tests" description="Compile unit tests">
<javac srcdir="${testdir}"
destdir="${targetdir}"
verbose="${TALK}"
classpathref="test.classpath">
</javac>
</target>
<target name="run-tests" depends="compile-tests" description="Compiles and runs unit tests">
<junit printsummary="yes" haltonfailure="true" showoutput="yes" >
<classpath refid="classpath" />
<classpath refid="test.classpath" />
<batchtest fork="yes" todir="${reportsdir}/raw/">
<formatter type="xml" />
<fileset dir="${testdir}" includes="**/AllTests.java" />
</batchtest>
</junit>
</target>
<target name="test" depends="run-tests" description="Comiles tests, runs them and generates test results in report format">
<junitreport todir="${reportsdir}">
<fileset dir="${reportsdir}/raw/">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="${reportsdir}\html\"/>
</junitreport>
</target>
<!-- end test related -->
<!--
<target name="package" depends="compile" description="Package up the project as a jar">
<jar destfile="ClaimsManager.jar">
<fileset dir="bin">
<include name="**/*.class"/>
</fileset>
<fileset dir="src">
<include name="**"/>
</fileset>
<fileset dir="test">
<include name="**"/>
</fileset>
</jar>
</target>
-->
<!--
<target name="all" depends="package"/>
-->
</project>
Feel free to suggest any ways to make this simpler and more automated.
No Comments // Posted on 12 July, 2010 // Software Development, Uncategorized
I have been using subversion for a number of years and it wasn’t until I had seen and sampled the simplicity and power of git that I decided once and for all to bite the bullet and migrate all my subversion repositories to git.
I’d done the same from cvs to subversion in the early 2000′s and still think the move worked out well with a bit of preparation.
An unfortunate side effect of subversion is that it takes a good bit of effort to set up and manage a new repository so I ended up with two somewhat monolithic repositories…
1. archives
2. projects
The ‘archives ‘ repository contained all old project work and the ‘projects’ one contained relevant or current project work.
The result of this was that each archive had a bundle of sub-folders, each containing a project in its’ own right. Although git can handle the concept of sub-modules it’s not the best way to structure your project. In fact, it was a pretty lazy to structure my subversion projects in the first place but convenience overcame system administrative chores at the time.
This article explains the following:
1. Converting a monolithic subversion repository to a git repository
2. Breaking out the new git repository into a set of discrete repositories.
3. General backup process and scripts to copy the new repositories across to a backup system.
First off, a couple of conventions I keep are…
My server stores a central set of repositories with the name of myproject.git
My workstations work with their local version of repositories called myproject (no .git)
My server exports it’s .git repositories to a backup server using the same .git naming convention.
I have created a git.git user.group on my server to run all git processes. File ownership is given to this user so other users can’t fowl things up at least without thinking about it first.
Step 1 – Migrating to subversion
I store all my git repositories on a server in the directory
/usr/local/share/gitrepos
All of my subversion repositories are stored in
/usr/local/share/svn
Even though each folder is accessible using standard paths and commands, I have to go through the subversion door using the same mechanism I would access it from a remote box, namely http://. Other people may use svn:// if that’s their way of working.
Migration is taken care of with a single command.
sudo git svn clone https://localhost/repository/projects --stdlayout --authors-file=/home/peter/authors.txt -t tags -b branches -T trunk /usr/local/share/gitrepos/projects.git/
An explanation of all the bits follow:
sudo – because I run everything as unprivileged user, sudo gives me the rights i need to create new files and folders.
git svn – this is the git subcommand that manages conversion of subversion repositories.
clone – make a copy of the repositories that I’m pointing at.
https://localhost/repository/projects – I access all my subversion repositories using secure http. This allows me to securely browse the content across the Internet and track things easily using Trac.
Depending on your setup, this parameter will contain the path to your svn repository,however you access it.
–stdlayout option tells git svn that my subversion repository is in the standard layout of trunk/branch/tags directories.
–authors file – this is a file I created by hand containing a list of all the people who committed project material in the past. It’d format is as follows:
peter= Peter Mac Giollafhearga <my email at mydomain.com>
simon= Simon Shagwell <simon\'s email at his domain.com>
-t tags -b branches -T trunk – these values are for completeness and I’m not sure they are necessary given the –stdlayout option, but if you’ve called your branches, tags and trunks anything different, this is how you find it.
Once the command has completed you should find a projects.git folder has been created and navigating into it you will see all your nasty subversion sub-folders which you should have set up as individual repositories in the first place…tut tut!
Step 2 – Breaking out Git into baby gits
The structure of the new git repository is something like this.
gitrepository/
projects.git/
project1/
WorldDomination/
SomeStuff/
SomeotherStuff_V2/
Demos/
Downloads/
.git/
The .git folder (you cans see it using ls -a) contains a list of all the git related material such as project history, revisions and tags.
Check you can see your history by typing
The next task is to break the contents of the rather large ‘project’ folder into a git repository per project.
The tool for the job is a combination of the very useful git subtree command and a bit of custom shell scripting specific to this job.
The subtree functionality was written by Avery Pennarun and is hosted by the github site. You can download the git subtree command from the URL http://github.com/apenwarr/git-subtree. Installation instructions are on the same site so I won’t bore you with it here.
Once you have it installed you’re almost ready to roll. The following shell script has comments at the top to explain what it does. Save this to your favourite scripts folder and chmod it so it’s executable (chmod +x git_breakout.sh).
Then cd into the folder you created earlier (in my case /usr/local/share/gitrepository/projects). Run the script and watch the output. Depending on the size of your projects it will take a bit of time to complete; mine took about 20 minutes.
#!/bin/sh
#git_breakout.sh
#This script should be run from within a git repository folder that
#contains many child folders.
#It will create a branch for every subfolder it finds and a new
#top level folder for each.
#It then initialises a git respoitory, copying into the relevant branch
# Make sure only root can run our script
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root" 1>&2
exit 1
fi
current_dir=`echo ${PWD##*/}`
git_user=git
git_group=git
for project in $( ls . )
do
if [ -d $project ]; then
project_lower=`echo $project.git | tr [A-Z] [a-z]`
branchname=$project_lower."export"
echo "performing subtree split..."
git subtree split -P $project -b $branchname
mkdir ../$project_lower
cd ../$project_lower
echo "initialising git"
git init
echo "fetching branch"
git fetch ../$current_dir $branchname
git checkout -b master FETCH_HEAD 2>&1
cd ..
echo "setting appropriate ownership"
chown $git_user.$git_group -R $project_lower
cd $current_dir
fi
done
Once the script has completed, you should be able to see a new directory structure, something along the following lines.
gitrepository/
projects.git/
project1.git
worlddomination.git/
somestuff.git/
someotherstuff_v2.git/
demos.git/
downloads.git/
Each folder is now its’ own git repository with it’s own internal version history, tags, branches etc.
cd into one of the folders and do a git log just to confirm you still have history.
The original projects.git can be removed (after backing it up for safety’s sake) if you’re satisfied everything is in place. We’re now ready to proceed with the next stage of the game, namely backing up our new git repositories to a remote share.
Step 3 – Backing up Git
I use a 2TB removable disk as a central data server for sharing files throughout my network. My thinking is that should the place ever burn down and I have the opportunity, there’s only one box I need to grab before rushing out the door. Of course, I burn frequent snapshots of this box to DVD.
I have created a ‘backups’ share on the disk which I map to my physical server using NFS.
The entry in my /etc/fstab file is something like this.
192.168.0.20:/DataVolume/backups /usr/local/share/backups nfs defaults 0 0
This means I can read/write to my backups folder as if it was a local folder on a local disk.
The job at hand is to be able to backup my git repositories using a cron task scheduled to run when everything is nice and quiet. Below is the script I use to run my git backups.
#!/bin/bash
# git_backup.sh
# Backup git repositories to another folder
# Make sure only root can run our script
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root" 1>&2
exit 1
fi
git_command=/usr/bin/git
#Where they're going to be backed up
backup_dir=/usr/local/share/backups/git
#Location of current live repositories
repository_dir=/usr/local/share/gitrepos
cd $repository_dir
for project in $( ls . )
do
if [ -d $project ]; then
destination="$backup_dir/$project"
if [ -d $destination ]; then
cd $destination
echo "pulling $project..."
$git_command pull
echo "done."
cd $repository_dir
else
echo "mkdir $destination"
mkdir $destination
cd "$repository_dir/$project"
echo "cloning $project..."
$git_command clone -v -l --no-hardlinks . $destination
cd ..
echo "done."
fi
fi
done
Once this script is run, you ‘ll have a copy of all your repositories in the backup folder. The next time it’s run should only take a fraction of the time as it won’t have to reestablish the git repository again.
So there you have it, migrating a subversion repository to git is really simple, the fun starts when you try to play with the results. I hope this has been helpful to others faced with the same job. Any improvements please let me know.
No Comments // Posted on 10 July, 2010 // Git, Linux, Software Development
I’ve just finished reading a little gem of a book. It’s called ‘The Martian Principles for Successful Enterprise Systems’ with a subtitle of ’20 Lessons Learned from NASA’s Mars Exploration Rover Mission’. The author is Ronald Mak.
Imagine designing an information retrieval, indexing and presentation system for the two Mars rover vehicles that were sent on a one-way reconnaissance mission to Mars for a three month mission. The feisty little vehicles kept going for two years and the information systems had to be designed to cope with this unexpected project over-run.
The book runs to 168 pages and is a ‘should-read’ for anybody involved in designing or buying large-scale enterprise software. From an architect’s perspective, you get a reinforced mental checklist of the aspects of your designs that make them work and ensure they keep working long after you’ve moved on. From a customer’s perspective, you gain an appreciation of the effort put into designing such systems. From a developer’s perspective, now you know why you spend so much time writing and executing unit tests.
The book has short and well directed chapters and is an easy read with coverage of both the technical side of software development and the soft or human side.
As a result of this read, I went back to enhance some application logging classes that I’ve used on a number of projects to provide more granular output and statistics on usage patterns.
No Comments // Posted on 31 July, 2007 // Books, Software Development
The attached files contain binaries and source code for a .Net Compact Framework application we built to enable the input of petrol consumption details and the subsequent downloading of that information to a desktop application. It was written in C# under a Visual Studio 2005 project and makes use of a set of libraries provided by OpenNETCF Consulting.
To develop the application you will need to download the Smart Device Framework libraries from OPenNETCF and add a reference to them in your project. I have included a feature that allows you to dynamically upload all necessary components to your mobile device. This is a cool feature enabled using a .Net custom installer application.
Feel free to download and modify the application to suit your fancy.
Fuel Monitor 1.1.0 Source Files
Fuel Monitor 1.1.0 Binaries
No Comments // Posted on 9 November, 2006 // Software Development, dotnet
In this world of bad guys and hackers and crackers, us developers have to be on our guard against all sorts of stuff that tries to break our websites. Validating input using JavaScript alone is not sufficient to gurad against the baddies as it can be worked around by building their own custom forms to interact with your site and/or disabling JavaScript.
What this means is that every piece of data you accept from a user should be validated against a set of rules for that datatype.
Some general validation rules
If it’s a text string, does it contain any less than or greater than brackets?
If it’s a date, are it’s day, monthy, year parts valid numbers?
If it’s a credit card number, is there an online number validation algorithm you can use to verify it?
Regular Expressions in PHP
In order to validate your user-input, you’ll generally find yourself resorting to regular expressions. In PHP there are two options.ereg and preg. ereg is older, less sophisticated and somewhat slower than it’s younger brother. My preference is to use the preg options even though they’re a little more complicated, the end results is betterer(sic).
An example of using preg to validate a date:
$month = $_GET['cardmonth'];
$year = $_GET['cardyear'];
if (!preg_match(“/^[0-9]{1,2}$/”, $month)) die(“Invalid month, please re-enter.”);
if (!preg_match(“/^[0-9]{4}$/”, $year)) die(“Invalid year, please re-enter.”);
An online manual is available from the main php website at http://www.php.net/manual/en/ref.pcre.php
And some lessons explicitly on how to use the preg function is available here http://www.php.net/manual/en/function.preg-match.php
1 Comment // Posted on 10 October, 2006 // Software Development
I’ve worked on a bundle of web based applications over the years and time and time again I’ve seen the recurring problem of the slash. Yes, we’ve probably all seen it in one or more forums where the apostrophe some user entered, probably with the name O’Brein ends up as O\\Brein.
Why does this happen in sites running on PHP? The answer is a duplication of escapes. Yep, a Houdini Supreme.
Firstly a systems administrator has installed PHP and set the value for magic_quotes_gpc = on in the system’s php.ini (usually located in /etc/). This will automatically add slashes to all GET/POST/COOKIE data. This makes it safe before writing it to a database. Mr O’Brein becomes Mr O\\’Brein when magic_quotes_gpc is set to on.
Secondly, a programmer has come along and thinking they’re doing the right thing takes all user input and uses the addslashes() funtion to escape all quotes. This results in a doubling of the escapes so, Mr O’\\Brien now becomes MR O\\\\’Brein.
When this data is rendered, we see the automatic removal of only one set of escapes but the other set is left behind…yuck!!
When programmers see this they think…”I’ll just use the stripslashes() method, I mean, that’s what it’s there for”. As the light from the idea bulb fades, they realise they’re fixing a problem that should never have occurred in the first place. You need to go to the source of your data and clean it up, make sure you’re either using magic_quotes_gpc=on OR addslashes. My preference is to use addslashes all the time and turn magic_quotes_gpc off, this way the logic of your code explicitly sets user input to be what you want.
No Comments // Posted on // PHP, Software Development