Archive for the ‘Programming’ Category
Gradle Android Plugin
I have recently joined a newly formed team developing Android applications at a large telco, and I am pleased to announce that we are using Gradle for our builds. We are using Gradle with the Android plugin, and instantly we managed to build a simple application, run tests, and have it installed on a device. Our build script simply looks like the following, which is all that is necessary to use the Android plugin.
buildscript {
repositories {
mavenRepo(urls: 'http://jvoegele.com/maven2/')
}
dependencies {
classpath 'com.jvoegele.gradle.plugins:android-plugin:0.8'
}
}
usePlugin com.jvoegele.gradle.plugins.android.AndroidPlugin
Of course this is a rather simplistic script, but it does everything I need it to do right out of the box. The Android plugin provides a number of tasks that allow you to build, test, package and sign your application. You can even install the packaged application on a device or emulator by running gradle androidInstall. Make sure to set the property “adb.device.arg” to “-e” for a running emulator or “-d” for a connected device.
There is also support in Hudson to trigger a Gradle script. Hudson has a Gradle plugin that can be installed from the Admin console, and allows you to directly trigger a Gradle script in your project. Otherwise you can create a simple shell script to call the Gradle tool from the command line.
It is also worth noting that both IntelliJ and Eclipse provide support for Gradle and the Groovy syntax. That is if you don’t like using the command line to trigger your builds.
Gradle has allowed us to spend less time setting up our build and continuous integration environment, and more time on actual Android development. Our team has benefited greatly from this boost in productivity.
Illuminate
My latest iPhone app called Illuminate is now available at Apple’s App store: http://bit.ly/bByrgy.
Illuminate is a free application that turns your iPhone’s screen into a light source. I generally use it when trying to read a menu at a dimly lit restaurant. Hopefully you will find it useful too!
One click deployment with Maven and Bamboo
A while back I wrote about achieving continuous deployment with one-click deployments. I didn’t provide an example for that post as I mostly wrote about why you need to achieve continuous deployment. Here I will follow up with a simple example of how you can achieve continuous deployment.
Continuous deployment is quite easy to setup if you are using a typical Maven project structure and Bamboo as your continuous integration tool. Also I am assuming that you want to deploy your application to a tomcat server.
In your pom.xml file add the following configuration so that you use the Tomcat plugin for deploying your application to http://hostname.com/app. Change the path, url, and server configurations to suit your needs.
org.codehaus.mojo tomcat-maven-plugin /app http://hostname.com/manager deployment.server
Also make sure that your .m2/settings.xml file contains the following for authenticating with the Tomcat manager.
deployment.server tomcat password
In Bamboo create a new pan for your project. I tend to give this plan the name “Promote to Production”. Configure the Builder goal for Maven to run clean tomcat:redeploy. I also only allow a specific user to trigger this plan so that not everyone has permission to deploy into production. Finally, configure the build strategy to run manually, so an authorised person can click on the Build plan button in Bamboo to deploy the application.
Once set up the above instructions will allow an authorised person in Bamboo to click on a single button to deploy into production. Leveraging your continuous integration tool for deployment allows you to archive deployment artifacts such as your WAR files in the case where you have to revert to a previous version.
Deploying Maven modules
My most recent consulting gig brought me back into contact again with Maven. I have this love-hate relationship with Maven. I love using Maven for simple projects, that generally have only a development environment, and I hate using Maven for more complex projects where you have to create profiles for multiple testing environments. Maven is just too restrictive for the latter case.
If you have your own internal Maven repository setup and you want to deploy reusable modules to it then follow these instructions. It took me a while to figure them out as there is something wrong with Wagon’s internal ssh implementation. Using an external scp tool will save you a lot of grief.
1. Setup your local .m2/settings.xml as follows:
internal.maven.repos maven location of your ssh private key
You’ll need to create an ssh key. And provide a reference to your private key using the
2. Log into your server hosting your maven repository and add the contents of your public key (id_rsa.pub) at the bottom of the authorized_keys2 file. You should create a generic maven user that everyone on your team can log into, this is because when you deploy your module it will be written to the filesystem with read/write permissions only for that user. Exit your server.
3. Go to your project and add the following to your project’s pom.
... internal.maven.repos Internal Maven Repository scpexe://maven@hostname.com/home/maven/.m2/repository org.apache.maven.wagon wagon-ssh-external 1.0-beta-2
4. Run mvn deploy.
5. You should now see your project deployed to the maven repository on your server.
Cloning a Java object
Here is a simple and effective approach to cloning objects in Java using XStream. The process uses XStream to serialize your object to XML, and then using the XML to create a new Java object that is a deep copy of the original.
Sheep dolly = new Sheep("Dolly");
XStream xstream = new XStream();
String xml = xstream.toXML(dolly);
Sheep newDolly = (Sheep)xstream.fromXML(xml);
Atlassian Starter License
I’m back at the University of Sydney doing some independent Agile and TDD coaching. The first thing that I wanted to set up was a CI server. My initial recommendation was Atlassian’s Bamboo product, which may come as a surprise given that I used to work for ThoughtWorks – the makers of Cruise – for the last three years. But I must confess, I have never actually used Cruise on a project, it was always Bamboo, Hudson, or Luntbuild. So I figured stick with what you know.
I thought the research group would be strapped for cash and considered setting up Hudson, but Atlassian’s Starter license gives you access to their products for an amazingly low price of $10 per product. It was easy to convince the group to use Bamboo for that price. Best of all the proceeds of the purchase price go towards a charity called Room to Read. Thank you Atlassian for your generosity!
Git on Joyent Shared Accelerator
This is just a quick guide to setting up a remote Git repository on a Joyent shared accelerator.
Log into your Joyent server and create your remote git repository.
ssh host.joyent.us
cd ~/git
mkdir project.git
cd project.git
git --bare init
chmod -x hooks
exit
Now on your local box create your project directory and push the source files to your newly created remote repository.
rails project
cd project
git init
git add .
git commit -a
git remote add origin ssh://host.joyent.us/home/username/git/project.git
git push origin master
Now get your collaborators to clone your repository and push their changes. Assuming you have added them as users via virtualmin.
git clone ssh://host.joyent.us/home/username/git/project.git
Estimation Deck
My Estimation Deck iPhone application just got published in the iTunes store! You can download it for free and use it for your next Agile estimation session. It saves me from carrying around a deck of cards, and I hope it saves you from that pain as well!
Agile Australia 2009 iPhone App
I have been working as part of a team at ThoughtWorks to build an iPhone app for the upcoming Agile Australia 2009 conference. It has been an anxious wait for Apple to approve the app, but it was a thrill when that email finally came through. I am pleased to announce that the Agile Australia 2009 iphone app is now available for download at the iTunes store for free! If you are attending the conference then this companion app will keep you informed of the schedule, provide feedback, follow the hot sessions, find your rooms, and tweet about the talks.
Deploying your Django app on Joyent Shared Accelerators
This guide is basically a rehash of this posting in Joyent’s support forums. I am reproducing it below to include my experiences as I found some discrepancies with the posting in the Joyent forums.
Joyent Shared Accelerators don’t allow you to deploy your Django app using mod_python, so you have to create a proxy path that diverts traffic to lighttpd and FastCGI to serve your Django app.
For this guide you will need to replace ${USER} with your username on Joyent’s server, the hostname of your server as ${HOST}, and your DNS domain as ${DOMAIN}.
Set up Apache as a proxy server for lighttpd
You no longer need to submit a support ticket to request a port number for lighttpd. You can just go to Virtualmin for your server (https://virtualmin.joyent.us/${HOST}/) > Other Tools > Check ports to view a list of available port numbers that have been reserved for you. Pick one and note it down. We will refer to this port number as ${PORT}.
Set up a directory structure for lighttpd:
mkdir -p ~/etc/init.d mkdir -p ~/etc/lighttpd/vhosts.d touch ~/logs/lighttpd.error.log ~/logs/lighttpd.access.log
Using a text editor, create ~/etc/lighttpd/lighttpd.conf:
#-- Lighttpd modules
server.modules = ( "mod_rewrite",
"mod_redirect",
"mod_access",
"mod_cgi",
"mod_fastcgi",
"mod_compress",
"mod_accesslog",
"mod_alias" )
#-- Fundamental process configs
server.port = ${PORT}
server.username = "${USER}"
server.groupname = server.username
var.base = "/users/home/" + server.username
server.pid-file = base + "/var/run/lighttpd.pid"
#-- Logging
server.errorlog = base + "/logs/lighttpd.error.log"
accesslog.filename = base + "/logs/lighttpd.access.log"
#-- Default
server.document-root = base + "/web/public"
server.indexfiles = ( "index.php", "index.html", "index.htm", "default.htm" )
#-- Security
url.access-deny = ( "~", ".inc", ".ht" )
#-- Mimetypes
include_shell "cat " + base + "/etc/lighttpd_mimetypes.conf"
#-- VHOSTS
Create ~/etc/lighttpd/mimetypes.conf:
mimetype.assign = ( ".pdf" => "application/pdf", ".sig" => "application/pgp-signature", ".spl" => "application/futuresplash", ".class" => "application/octet-stream", ".ps" => "application/postscript", ".torrent" => "application/x-bittorrent", ".dvi" => "application/x-dvi", ".gz" => "application/x-gzip", ".pac" => "application/x-ns-proxy-autoconfig", ".swf" => "application/x-shockwave-flash", ".tar.gz" => "application/x-tgz", ".tgz" => "application/x-tgz", ".tar" => "application/x-tar", ".zip" => "application/zip", ".mp3" => "audio/mpeg", ".m3u" => "audio/x-mpegurl", ".wma" => "audio/x-ms-wma", ".wax" => "audio/x-ms-wax", ".ogg" => "audio/x-wav", ".wav" => "audio/x-wav", ".gif" => "image/gif", ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg", ".png" => "image/png", ".xbm" => "image/x-xbitmap", ".xpm" => "image/x-xpixmap", ".xwd" => "image/x-xwindowdump", ".css" => "text/css", ".html" => "text/html", ".htm" => "text/html", ".js" => "text/javascript", ".asc" => "text/plain", ".c" => "text/plain", ".conf" => "text/plain", ".text" => "text/plain", ".txt" => "text/plain", ".dtd" => "text/xml", ".xml" => "text/xml", ".mpeg" => "video/mpeg", ".mpg" => "video/mpeg", ".mov" => "video/quicktime", ".qt" => "video/quicktime", ".avi" => "video/x-msvideo", ".asf" => "video/x-ms-asf", ".asx" => "video/x-ms-asf", ".wmv" => "video/x-ms-wmv", ".bz2" => "application/x-bzip", ".tbz" => "application/x-bzip-compressed-tar", ".tar.bz2" => "application/x-bzip-compressed-tar" )
Finally, create an init script at ~/etc/init.d/lighttpd:
#!/bin/sh
HOME=/users/home/${USER}
LIGHTTPD_CONF=$HOME/etc/lighttpd/lighttpd.conf
PIDFILE=$HOME/var/run/lighttpd.pid
case "$1" in
start)
# Starts the lighttpd daemon
echo "Starting lighttpd"
PATH=$PATH:/usr/local/bin /usr/local/sbin/lighttpd -f $LIGHTTPD_CONF
;;
stop)
# stops the daemon bt cat'ing the pidfile
echo "Stopping lighttpd"
kill `/bin/cat $PIDFILE`
;;
restart)
## Stop the service regardless of whether it was
## running or not, start it again.
echo "Restarting lighttpd"
$0 stop
$0 start
;;
reload)
# reloads the config file by sending HUP
echo "Reloading config"
kill -HUP `/bin/cat $PIDFILE`
;;
*)
echo "Usage: lighttpd (start|stop|restart|reload)"
exit 1
;;
esac
Don’t forget to make the init script executable:
chmod 755 ~/etc/init.d/lighttpd
Proxy Apache to lighttpd
Open up a web browser, and log into https://virtualmin.joyent.us/${HOST}/
Select the virtual server to configure. Then go to Server Configuration > Proxy Paths > Add a new proxy path. Enter the following values and click Create.
Local URL path: /
Destination URLs: http://${DOMAIN}:${PORT}
Configure Django environment
Add the path to your Django app to the PYTHONPATH. Add the following to your .profile and .bashrc files.
export PYTHONPATH=/users/home/${USER}/src/django_projects
Deploying your Django app
Check out your django app to /users/home/${USER}/src/django_projects. I will refer to this django app as ${APPNAME}.
cd ~/src/django_projects
svn co svn+ssh://subversion_repos/site/${APPNAME}/trunk ${APPNAME}
Create a MySQL database
Normally I would use PostgreSQL cos it rocks, but unfortunately Joyent only provides database restrictions for specific users on MySQL. So we’ll create a mysql user and grant it privileges to access a mysql database.
Open up a web browser and log into https://virtualmin.joyent.us/${HOST}/
Select ${DOMAIN} from the dropdown list > click “Edit Databases” > Click “Create a new database”.
I entered “production” into the Database name field so that my database will be called ${USER}_${DOMAIN}_production. Then click “Create”.
Create a database user
In Virtualmin, Select ${DOMAIN} from the dropdown list.
Click “Edit Mail and FTP Users” > “Add a user to this server”.
Under Virtual domain user mailbox details, enter “django” into the Email address field. This will create a mysql user called django-${DOMAIN}, and the auto-generated password will also be used for the mysql password in your django settings.py.
Expand “Quota and home directory settings”. Limit the user’s home directory quota to 1MB.
Expand “Other user permissions”. Allow the user access to the “${USER}_${DOMAIN}_production” database we just created. Click create.
Configure project settings
In settings.py modify your database settings to the following:
FORCE_SCRIPT_NAME=''
import os.path
ROOT_DIR = os.path.abspath(os.path.dirname(file))
DATABASE_ENGINE = 'mysql'
DATABASE_NAME = '${USER}_${DOMAIN}_production'
DATABASE_USER = 'django-${DOMAIN}'
DATABASE_PASSWORD = 'password"
DATABASE_HOST = ''
DATABASE_PORT = ''
MEDIA_ROOT = os.path.join(ROOT_DIR, 'media')
MEDIA_URL = '/media/'
ADMIN_MEDIA_PREFIX = '/media/admin/'
TEMPLATE_DIRS = (
os.path.join(ROOT_DIR, 'templates'),
)
INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.flatpages',
)
Since the settings file has our MySQL password inside, don’t let others read it:
chmod 600 ~/src/django_projects/${APPNAME}/settings.py
Then create the database tables in the usual fashion:
./manage.py syncdb
Create project init script
Create ~/src/djangoprojects/${APPNAME}/etc/init.sh:
#!/bin/sh
HOME="/users/home/${USER}" # Edit to your own username
PYTHONPATH=$HOME/src/django_projects
export PYTHONPATH
PROJECT_NAME="${APPNAME}"
PROJECT_DIR="$HOME/src/django_projects/$PROJECT_NAME"
PID_FILE="$HOME/var/run/$PROJECT_NAME.pid"
SOCKET_FILE="$HOME/tmp/$PROJECT_NAME.socket"
MANAGE_FILE="$PROJECT_DIR/manage.py"
METHOD="prefork"
case "$1" in
start)
# Starts the Django process
echo "Starting Django project $PROJECT_NAME"
python $MANAGE_FILE runfcgi maxchildren=2 maxspare=2 minspare=1 method=$METHOD socket=$SOCKET_FILE pidfile=$PID_FILE
;;
stop)
# stops the daemon by cat'ing the pidfile
echo "Stopping Django project $PROJECT_NAME"
kill `/bin/cat $PID_FILE`
;;
restart)
## Stop the service regardless of whether it was
## running or not, start it again.
echo "Restarting Django project $PROJECT_NAME"
$0 stop
$0 start
;;
*)
echo "Usage: init.sh (start|stop|restart)"
exit 1
;;
esac
Make the init script executable:
chmod 755 ~/src/djangoprojects/${APPNAME}/etc/init.sh
Offload static media to lighttpd
We don’t want Django to be serving static content, so any path that refers to static content will be served by the web server directly from ~/web/public.
Create a softlink from django’s admin media to ~/web/public/media/admin.
mkdir ~/web/public/media ln -s /usr/local/lib/python2.5/site-packages/django/contrib/admin/media/ ~/web/public/media/admin
Create a softlink from your project’s media directory to ~/web/public/media/public.
mkdir -p ~/src/django_projects/project/media/public
ln -s /users/home/${USER}/src/django_projects/${APPNAME}/media/public ~/web/public/media/public
Configure lighttpd
Edit ~/etc/lighttpd/vhosts.d/${APPNAME}.conf.
$HTTP["host"] =~ "(www.)?${DOMAIN}" {
server.document-root = base + "/web/public"
fastcgi.server = (
"/${APPNAME}.fcgi" => (
"main" => (
"socket" => base + "/tmp/${APPNAME}.socket",
"bin-environment" =>
( "TZ" => "America/Chicago" ),
"check-local" => "disable",
)
),
)
url.rewrite-once = (
"^(/media/admin.*)$" => "$1",
"^(/media/public.*)$" => "$1",
"^/favicon.ico$" => "/media/public/img/favicon.ico",
"^(/.*)$" => "/${APPNAME}.fcgi$1",
)
}
Then include vhosts.d/${APPNAME}.conf in your lighttpd.conf:
echo 'include "vhosts.d/${APPNAME}.conf"' >> ~/etc/lighttpd/lighttpd.conf
Schedule service start
Create a Joyent bootup action in Virtualmin. In Virtualmin, Select ${DOMAIN} from the dropdown list > go to Services > Booup Actions > Add a new bootup action. Enter the following values in the input fields and click “Create”.
Action name: init-${APPNAME}-django-site
Description: Init ${APPNAME} Django Site
Commands to run at startup: /users/home/${USER}/src/django_projects/${APPDNAME}/etc/init.sh start
Go back to the Bootup Actions, click “Add lighttpd”. Enter the following values in the input fields and click “Create”.
Action name: lighttpd-${APPNAME}-django-site
Description: Lighttpd ${APPNAME} Django Site
Commands to run at startup: /usr/local/sbin/lighttpd -f /users/home/${USER}/etc/lighttpd/lighttpd.conf
Open up your browser and go to your newly deployed Django app!
If you don’t see your site then you will have to do some debugging. I didn’t get it first time as you’ll note that my instructions above are slightly different from the original post here.
