Tuesday, 19 April 2011

Currency Symbols

Had an issue recently where some unit tests that were working on Windows didn't work on OSX. This snippet sums up why:
public void testCurrencySymbol() {
Locale locale = new Locale("en", "GB");
String formattedPrice = Currency.getInstance("EUR").getSymbol(locale) + "10.00";
String badFormattedPrice = Currency.getInstance("EUR").getSymbol() + "10.00";
assertEquals(formattedPrice, badFormattedPrice); // fails on OSX!
}

Seems a bit weird, but it's sort of logical - you have to pass in your target locale when you get the currency symbol for some other locale.

Wednesday, 22 December 2010

Implementing a Process Timeout

I had a slightly unusual requirement to implement recently where I need to execute a command line process from Java + have it timeout if it didn't complete within a given timeframe.

Having considered a rather messy solution involving threads, joins, etc, I stumbled across the Executor framework that appeared in Java 1.5. Although the concepts seemed a little abstract at first, it provided a fairly elegant way of doing it:
public void executeCommand(String command, long timeoutInSeconds) throws IOException,
InterruptedException, ProcessException {
ExecutorService service = Executors.newSingleThreadExecutor();
Process process = Runtime.getRuntime().exec(command);
try {
Callable call = new CallableProcess(process);
Future future = service.submit(call);
int exitValue = future.get(timeoutInSeconds, TimeUnit.SECONDS);
if (exitValue != 0) {
throw new ProcessException("Process did not exit correctly");
}
}
catch (ExecutionException e) {
throw new ProcessException("Process failed to execute", e);
}
catch (TimeoutException e) {
process.destroy();
throw new ProcessException("Process timed out", e);
}
finally {
service.shutdown();
}
}

private class CallableProcess implements Callable {

private Process p;

public CallableProcess(Process process) {
p = process;
}

public Integer call() throws Exception {
return p.waitFor();
}
}

Best of all, it actually works. :-)

Monday, 26 July 2010

Spring Schemas

I ran into an interesting problem with Spring and ActiveMQ a few days ago. To make use of the ActiveMQ namespace in our Spring contexts, I had the following beans element:
  xmlns="http://www.springframework.org/schema/beans"
amq="http://activemq.apache.org/schema/core"
xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.1.0.xsd">

...

This seemed fairly sensible since we're using version 5.1.0 of ActiveMQ and there IS a schema at http://activemq.apache.org/schema/core/activemq-core-5.1.0.xsd.

However, when this was deployed to one of our test environments that does NOT have access to the internet, it fell over with the following exception:
Offending resource: class path resource [appContext.xml];
nested exception is org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
Line 10 in XML document from class path resource [messagingContext.xml] is invalid;
nested exception is org.xml.sax.SAXParseException: cvc-complex-type.2.4.c:
The matching wildcard is strict, but no declaration can be found for element 'amq:connectionFactory'
This led me to discover a feature that I was previously unaware of - the spring.schemas file. The documentation says this:
The properties file called 'spring.schemas' contains a mapping of XML Schema locations (referred to along with the schema declaration in XML files that use the schema as part of the 'xsi:schemaLocation' attribute) to classpath resources. This file is needed to prevent Spring from absolutely having to use a default EntityResolver that requires Internet access to retrieve the schema file.
In fact, the default spring.schemas file contains the following entries:
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
So both those URL's are mapped to schemas on the classpath, ie. Spring doesn't need to download them from the internet.

However, the spring.schemas file bundled with ActiveMQ contains these entries:
http\://activemq.org/config/1.0=activemq.xsd
http\://activemq.org/config/1.0/1.0.xsd=activemq.xsd
http\://activemq.apache.org/schema/core=activemq.xsd
http\://activemq.apache.org/schema/core/activemq-core.xsd=activemq.xsd
So although mappings are provided, the mappings are for URL's that don't actually exist. (To be honest, I can't see how/why you would use these mappings unless you were already aware of this feature!)

Nonetheless, I fixed the issue by changing the beans declaration as follows:
  xmlns="http://www.springframework.org/schema/beans"
amq="http://activemq.apache.org/schema/core"
xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

...

Note that the URL for the ActiveMQ namespace now matches a URL in spring.schemas.

Thursday, 15 July 2010

Tapestry: Block Delegation

The following technique allows a page to inject content directly into a layout component. In our case, we're injecting metatags into the <head> element, but it could also be used for a breadcrumb trail, navigation panel, etc.

Layout.java contains this:
/*
* An odd but useful tapestry construct - note that the markup for this block is
* in the page itself - see ...
*/
@Property
@Parameter(defaultPrefix = BindingConstants.LITERAL)
private Block metatags;
Layout.tml contains this:


This leaves the page free to implement the metatags however it sees fit. For example in AtoZ.tml we have this:

<meta name="description" content="Find all of your favourite TV shows beginning with ${initial} online."/>
<meta name="keywords" content="${initial} shows, ${initial} shows watch, watch tv online"/>
Just remember you need to define the 'p' namespace as being xmlns:p="tapestry:parameter".

Monday, 10 May 2010

Ubuntu 10.04 : GRUB 2 Issue

I should start by saying that the latest Ubuntu is GREAT. The startup time puts Windows to shame (what exactly is Windows doing for all that time?) and wireless connectivity works flawlessly.

However, I got an unpleasant surprise when I installed it on my Acer laptop. The some reason, GRUB 2 mis-detected my Windows partitions and thought that the hidden recovery partition was the main Windows installation. When I attempted to boot into this partition, I got an unfamiliar Acer screen and, worse still, the Ubuntu partition was completely destroyed! (By completely destroyed I mean that GParted showed the partition as being unallocated.)

After re-installing Ubuntu and reading the GRUB 2 documentation, I came up with the following solution:

1. Disable the os-prober - this is the script that detects the windows partitions and adds them to grub.cfg. You can do this by simply adding the following option to /etc/default/grub:

GRUB_DISABLE_OS_PROBER="true"

2. Put a modified version of the windows menu entries in /etc/grub.d/40_custom - see here for more details.

3. Run update-grub - this recreates the grub.cfg file. In fact, grub.cfg is updated every time there's an update and that's why you should never edit it by hand.

Dual boot restored!

Monday, 12 April 2010

Java XPath

I was pleasantly surprised when I needed to do some XPath in Java recently - it's been a while since I did any and I was expecting some potentially tedious 3rd party integration. Turns out it's now all included in the standard JDK and extracting the data we need from an online resource that looks like this ...








...

Was simply a case of doing this ...
// get connection to items service
URL itemsUrl = new URL(ITEMS_URI);
URLConnection itemsConnection = itemsUrl.openConnection();

// use xpath to extract the item ID's
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(itemsConnection.getInputStream());
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression xpathExpression = xpath.compile("/guides/r/a[@n=\"ItemId\"]/@v");

// create the list of item ID's
List<Long> itemIds = new ArrayList<Long>();
Object result = xpathExpression.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
String itemId = nodes.item(i).getNodeValue();
try {
itemIds.add(Long.parseLong(itemId));
} catch (NumberFormatException e) {
System.out.println("Could not parse item ID: " + itemId);
}
}

return itemIds;
I'm probably being more specific than I need to be there, but I'm not 100% sure what the finished format will look like.

Friday, 12 February 2010

OneToMany Mapping

Sounds trivial, but it recently came to my attention that the OneToMany mappings in our app looked like this ...
@OneToMany(targetEntity = ChildItem.class, mappedBy = "parentId")
@NotFound(action = NotFoundAction.IGNORE)
public List<ChildItem> getChildItems() {
return childItems;
}
This worked (for a while) but it makes the assumption that the foreign key in the 'many' table is mapped to the primary key in the 'one' table. Of course, this caused us no end of grief when we discovered that the parent ID is non-unique and changed the table to include a real ID.

In fact, a more standard approach looks like this:
@OneToMany
@JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID")
@NotFound(action = NotFoundAction.IGNORE)
public List<ChildItem> getChildItems() {
return childItems;
}
The JoinColumn annotation is basically saying "join these tables by mapping the PARENT_ID in the child items table to PARENT_ID in the parent items table".