Installing Maven on a Mac

Current Mac OS X Snow Leopard comes with Maven 2.2.1 installed. Let’s go ahead and update it to the latest version 3.0.2.

First, download it from Apache Maven site.

Unzip the downloaded file into the desired folder. You can put it in /usr/local. However in my case I prefer /app folder.

Create a simple alias:

air:app dmitry$ ln -s apache-maven-3.0.2/ maven
air:app dmitry$ ls
apache-maven-3.0.2	eclipse			maven

We are almost done. Update your .profile to setup PATH

export M2_HOME=/apps/maven
export M2=$M2_HOME/bin
export PATH=$M2:$PATH

Source your .profile file and we are done. Let’s make sure of that:

air:~ dmitry$ mvn -version
Apache Maven 3.0.2 (r1056850; 2011-01-08 19:58:10-0500)
Java version: 1.6.0_22, vendor: Apple Inc.
Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
Default locale: en_US, platform encoding: MacRoman
OS name: "mac os x", version: "10.6.6", arch: "x86_64", family: "mac"
Java resource loading

Anytime one needs to load Java resources (XML, jpeg, PDF, etc) from the classpath, using Class.getResourceAsStream or ClassLoader.getResourceAsStream is the way to go. However, there seems to be some confusion on how each one of these methods deals with the path in URL. I hope that this detailed experiment will serve as a helpful reminder of what the requirements are for both methods.

To make things more interesting I’ve put together a little program that should demonstrate the results on practice. I’ve covered three scenarios for where the file may reside in a typical Java application.

Assume that directory structure looks like this:

/
/rootFile.txt
/src/
/src/srcFile.txt
/src/com/letor/example/
/src/com/letor/example/packageFile.txt
  • rootFile.txt resides in the root folder
  • srcFile.txt resides in the src folder, will be included in the classpath (alternative to this is your typical cls folder
  • packageFile.txt lies in the same package directory as the class invoking the call

The program I wrote looks like this:

package com.letor.example;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ResourceLoader 
{
	private static Logger log_ = Logger.getLogger(ResourceLoader.class.getName());
	
	public static String[] FILE_NAMES = { "rootFile.txt",
										  "/rootFile.txt",
										  "srcFile.txt",
										  "src/srcFile.txt",
										  "/src/srcFile.txt",
										  "packageFile.txt", 
										  "com/letor/example/packageFile.txt",
										  "/com/letor/example/packageFile.txt" };
	
	public static void main(String[] args)
	{
		StringBuilder sb = new StringBuilder();
		sb.append("\n");

		try
		{
			for (String filename : FILE_NAMES)
			{
				// Class
				sb.append("Class\t\t");
				InputStream in = ResourceLoader.class.getResourceAsStream(filename);
	
				if (in == null)
				{
					sb.append("NO");
				}
				else
				{
					sb.append("YES");
					in.close();
				}
	
				sb.append("\t[").append(filename).append("]\n");
				
				// ClassLoader
				sb.append("ClassLoader\t");
				in = ClassLoader.getSystemResourceAsStream(filename);
				
				if (in == null)
				{
					sb.append("NO");
				}
				else
				{
					sb.append("YES");
					in.close();
				}
	
				sb.append("\t[").append(filename).append("]\n");
			}
		}
		catch (IOException ioe)
		{
			log_.log(Level.SEVERE, "Failed to load a resource", ioe);
		}
		log_.info(sb.toString());
	}
}

Running the program produces the following output. Class indicates Class.getResourceAsStream() call. ClassLoader indicates ClassLoader.getSystemResourceAsStream() method.

Class		NO	[rootFile.txt]
ClassLoader	NO	[rootFile.txt]
Class		NO	[/rootFile.txt]
ClassLoader	NO	[/rootFile.txt]
Class		NO	[srcFile.txt]
ClassLoader	YES	[srcFile.txt]
Class		NO	[src/srcFile.txt]
ClassLoader	NO	[src/srcFile.txt]
Class		NO	[/src/srcFile.txt]
ClassLoader	NO	[/src/srcFile.txt]
Class		YES	[packageFile.txt]
ClassLoader	NO	[packageFile.txt]
Class		NO	[com/letor/example/packageFile.txt]
ClassLoader	YES	[com/letor/example/packageFile.txt]
Class		YES	[/com/letor/example/packageFile.txt]
ClassLoader	NO	[/com/letor/example/packageFile.txt]

Now let’s go over each one of the lines and figure out what is happening

Class		NO	[rootFile.txt]
ClassLoader	NO	[rootFile.txt]
Class		NO	[/rootFile.txt]
ClassLoader	NO	[/rootFile.txt]

In all four cases files are not visible to the class loader since the root directory is not in the classpath.

Class		NO	[srcFile.txt]
ClassLoader	YES	[srcFile.txt]

Class does not see this file since it looks in the same directory where it resides. ClassLoader, however starts at the root of the classpath and finds the file.

Class		NO	[src/srcFile.txt]
ClassLoader	NO	[src/srcFile.txt]
Class		NO	[/src/srcFile.txt]
ClassLoader	NO	[/src/srcFile.txt]

In all of these cases the file is not found. Notice that prepending slash to the path does not help.

Class		YES	[packageFile.txt]
ClassLoader	NO	[packageFile.txt]

This is the exact opposite of what we saw when looking for srcFile.txt. Here the Class finds the file since it is in the same package, but ClassLoader fails

Class		NO	[com/letor/example/packageFile.txt]
ClassLoader	YES	[com/letor/example/packageFile.txt]

The last four cases are the most interesting. Notice the inverse relationship. Here the Class fails to find the file since it starts looking from its own directory. ClassLoader looks from the classpath root and easily finds the file

Class		YES	[/com/letor/example/packageFile.txt]
ClassLoader	NO	[/com/letor/example/packageFile.txt]

Finally Class figures out what to do, since we’ve specified a slash. Now it knows to go back to the beginning of the classpath. ClassLoader is less lucky, that slash confuses it.

I hope that this has been as educational for you as it was for me. Let’s sum all of this up.

Class.getResourceAsStream() will always look in its own package directory first. The only way to tell it look elsewhere is to provide it with an absolute path

ClassLoader.getSystemResourceAsStream() will always start looking at the root of the classpath, but do not confuse it by a forward slash.

“Pragmatic Project Automation”

“Pragmatic Project Automation” does a good job of introducing us to the world of continuous software integration. It covers all aspects of the software lifecycle, mainly focusing on the build, deployment and monitoring. You will learn how to organize your software release process so as to utilize a number of automatic tools, services and techniques.

The end result should be a process where a large number of human errors during deployment would be caught or avoided, gaining better confidence in what has been released.

Technologies used in this book are: Java, Ant, CruiseControl, scripting, JUnit. There is talk of Maven, but I guess that it was not yet as prominent in 2004 as it is today. The same techniques can easily be applied to .NET, PHP, Python or any other development environment.

Since most examples assume a website for a project, I wish that there would be a section on how a client-server app with a database would fit this model.

One thing that I find awkward is the choice of Version Control, it is still CVS. This book was published in 2004, when SVN has already showed its superiority over CVS.

This book does contain everything one need to know to start using project automation at your shop. I would recommend a place on developer’s bookshelf for this book.

P.S. This book is still not available on Kindle. :(

Java @Override annotation usage

Since the start of using annotations I was not a big fan of using @Override annotation. It seemed to have happened overnight, where after a new version of Eclipse was installed I was presented with 400+ warnings telling me to put in the dreaded overrides.

I do think that this seems unnecessary since it clutters the code a lot. But having lots of unresolved warnings in your IDE is even worse. Yes, I know that the compiler warnings can be fined tuned, but there might be a reason this warning comes on by default. So what is it?

As it turns out there are a few benefits. One is that the compiler will perform the check for you if you’ve specified the @Override annotation and will ensure that you are in fact overriding a method. This means that you no longer will have errors in your code where you’ve mistyped method name or have a slightly different signature. This is quite a benefit, since finding some of these errors can turn out to be quite time consuming.

The second benefit is that it will indicate to a fellow developer that this method indeed overrides (or implements) another method.

Well, there you have it. I am changing my stance on this subject and am willing to give the @Override a try. 

“Failed to load JavaHL Library”

One may get the following error message when running Subclipse plugin in Eclipse on a Mac OS X.

Failed to load JavaHL Library.
These are the errors that were encountered:
no libsvnjavahl-1 in java.library.path
no svnjavahl-1 in java.library.path
no svnjavahl in java.library.path
java.library.path = .:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java

SVN will still work by some sort of magic, but the official reason for you getting this error is that Eclipse can not find the JavaHL library (under /Library/Java/Extensions). JavaHL is the Java language binding for the Subversion API.

The most painless option is to re-install Subversion as part of the binary package from CollabNet. This will also install JavaHL.

Unfortunately this download requires a registration. This registration is rather painful as well.

Notice that the link for “Snow Leopard” is not the first one. This is a surprising choice, since that is the most recent version of the OS to date.

Make sure to update your .profile file with the following line. Otherwise we will still be using the old version of Subversion.

#!/bin/sh
export PATH=/opt/subversion/bin:$PATH

After you’ve updated your .profile file, close your Terminal and open a new one. Make sure that you are using the latest version.

sturgeon-2:~ dmitry$ which svn
/opt/subversion/bin/svn
sturgeon-2:~ dmitry$ svn --version
svn, version 1.6.15 (r1038135)
   compiled Nov 29 2010, 16:11:54

Copyright (C) 2000-2009 CollabNet.
Subversion is open source software, see http://subversion.apache.org/
This product includes software developed by CollabNet (http://www.Collab.Net/).

The following repository access (RA) modules are available:

* ra_neon : Module for accessing a repository via WebDAV protocol using Neon.
  - handles 'http' scheme
  - handles 'https' scheme
* ra_svn : Module for accessing a repository using the svn network protocol.
  - with Cyrus SASL authentication
  - handles 'svn' scheme
* ra_local : Module for accessing a repository on local disk.
  - handles 'file' scheme
* ra_serf : Module for accessing a repository via WebDAV protocol using serf.
  - handles 'http' scheme
  - handles 'https' scheme

Restart your Eclipse and you are done. No more warnings. You can verify that you’ve got the right version of JavaHL installed by going into Preferences->Team->SVN. Under “SVN Interface” you will see the version of JavaHL that you are using.

Auto-generated code by Xcode puts the opening brace on the same line by default.

To change that you would have to overwrite the default template. Run this in the Terminal:

defaults write com.apple.Xcode XCCodeSenseFormattingOptions '{ "BlockSeparator" = "\n" ; }'
Snow Leopard, Apache and PHP

Mac OS X Snow Leopard comes with Apache web server preinstalled. It can be engaged by enabling “Web Sharing” under “Sharing” preferences.

To get PHP going on Snow Leopard’s Apache web server one would need to download the latest version of Xcode and install it. This will install PHP onto the system.

Now to tie the two together, visit the /etc/apache2/httpd.conf file and uncomment the line “LoadModule php5_module        libexec/apache2/libphp5.so”.

Restart the web server by disabling and then enabling the Web Sharing.

Java Timezones

A handy reference for all the timezones defined in Java.

Australian Central Daylight +10:30 CSuT, ACDT, CDT
Australian Central Standard +9:30 ACST, CAST, CST
Australian Eastern Daylight +11 AEDT, ESuT
Australian Eastern Standard +10 AES, AEST, EAST, EST
Australian South Daylight +10:30 SADT
Australian South Standard +9:30 SAST
Australian Western Daylight +9 AWDT, WADT, WDT
Australian Western Standard +8 AWST, WAST, WST
Austria +1 WUT
Azores (obsolete) +2 AT
Baghdad +3 BT
Belgium, Mid-European +1 MET, MEWT
Bering Summer (1967-1983) 11 BST
Bering (until 1967) 11 BT
Brazil 3 BRA, BST
Brazil Summer 4 Horario_De_Verao
British Summer +1 BST
Canada, Atlantic Daylight 3 ADT, ASTU
Canada, Atlantic Standard 4 AST, ADT
Canada, Central Daylight 5 CDT
Canada, Central Standard 6 CST
Canada, Eastern Daylight 4 EDT
Canada, Eastern Standard 5 EST
Canada, Mountain Daylight 6 MDT
Canada, Mountain Standard 7 MST
Canada, Newfoundland Daylight 2:30 NDT
Canada, Newfoundland Standard 3:30 NST, NFT
Canada, Pacific Daylight 7 PDT
Canada, Pacific Standard 8 PST
Canada, Yukon Daylight 8 YDT
Canada, Yukon Standard 9 YST
Central Alaskan Standard (until 1967) 10 CAT
China Coast (obsolete) +8 CCT
China Daylight +9 CDT
China Standard +8 CST
Dansk Normal +1 DNT
Dansk Summer +2 DST
Europaeische, Mittel Sommerzeit +2 MESZ
Europaeische, Mittel Zeit +1 MEZ
European, Central +1 CET
European, Central Daylight +2 CETDST
European, Central Summer +2 CEST
European, Eastern +2 EET
European, Eastern Summer +3 EEST, EETDST
European, Middle Daylight +2 MEDST, MEST
European, Middle +1 MET, MEWT
European - Prague, Vienna +1 SET
European, Western Summer +1 WEST, WETDST
European, Western 0 WET
Fernando de Noronha Daylight 1 FDT
Fernando de Noronha Standard 2 FST
French Summer +2 FST
French Winter +1 FWT
Greenland, Standard (obsolete) 3 GST
Greenland, Eastern Daylight 1 WTZ
Greenland, Eastern Standard 2 VTZ
Greenland, Western Standard 3 UTZ
Greenland, Western Daylight 2 VTZ
Greenwich 0 GMT
Guam Standard +10 GST
Hawaiian Daylight (until 1947) 9:30 HDT
Heure Fancais d’Ete +2 HFE
Heure Fancais d’Hiver +1 HFH
Hong Kong +8 HKT
Indian Standard +5:30 IST
International Date line, East +12 IDLE
International Date line, West 12 IDLW
Iran Daylight +4:30 IDT
Iran Standard +3:30 IST
Iran +3:30 IT
Israeli Daylight +3 IDT
Israeli Standard +2 IST
Italy +1 ITA
Japanese Standard +9 JST
Java +7:30 JT
Korean Daylight +10 KDT
Korean Standard +9 KST
Malaysia +8 MAL
Melbourne, Australia +10 LIGT
Mexico 6 MEX
Moluccas (obsolete) +8:30 MT
Moscow Summer +3 MSD
Moscow W inter +2 MSK
New Zealand Daylight +13 NZDT
New Zealand Standard +12 NZT, NZST
Newfoundland Daylight 2:30 NDT
Newfoundland Standard 3:30 NST
Nome (until 1967) 11 NT
North Sumatran (obsolete) +8:30 NST
Norway +1 NOR, EMT
Samoa Standard 11 SST
Singapore Standard +8 SST
South Sumatran (obsolete) +7 SST
Spain +1 HOE
Swedish Summer +2 SST
Swedish +1 SWT
Thailand Standard +7 THA
Turkish Standard +3 TST, MAT
Universal 0 UT
Universal Time Coordinate 0 UTC
West Africa (obsolete) +1 WAT
United States, Alaska-Hawaii 9 AHDT
     Daylight (1967 to 1983)
United States, Alaska-Hawaii 10 AHST
     Standard (1967 to 1983)
United States, Alaska Daylight 8 AKDT
United States, Alaska Standard 9 AKST
United States, Atlantic Daylight 3 ADT
United States, Atlantic Standard 4 AST
United States, Central Daylight 5 CDT
United States, Central Standard 6 CST
United States, Eastern Daylight 4 EDT
United States, Eastern Standard 5 EST
United States, Hawaiian Standard 10 HST, HAST (10:20 until 1947)
United States, Hawaiian Daylight 9 HADT
United States, Mountain Daylight 6 MDT
United States, Mountain Standard 7 MST
United States, Pacific Daylight 7 PDT
United States, Pacific Standard 8 PST