Friday, December 6, 2013

DbUnit WARN in logs "Extra columns on line 2. Those columns will be ignored."

WARN dbunit.dataset.xml.FlatXmlProducer - Extra columns on line 2.  Those columns will be ignored.
WARN dbunit.dataset.xml.FlatXmlProducer - Please add the extra columns to line 1, or use a DTD to make sure the value of those columns are populated or specify 'columnSensing=true' for your FlatXmlProducer.
WARN dbunit.dataset.xml.FlatXmlProducer - See FAQ for more details.

Problem was in XML file which defined data that should be loaded to database. Here is simplified version:

 <user id_user="1"/>

 <user id_user="2" login="john" />

Problem was in parameter columnSensing. During reading XML data with FlatXmlProducer parameter columnSensing was defined as false or wasn't defined which is treated as false. Let's look at meaning of columnSensing parameter.

This parameter define which columns will be inserted into tables. When parameter columnSensing is falseDbUnit reads xml elements defining data one by one. When new element is detected that all attribute are considered as column names. Next time when element with same name is found inserted into database table are just already columns. When columnSensing is true than columns will be inserted into database table even if they are not defined in first XML element.

Solution 1.

First solution of previous example is quite easy just set columnSensing parameter to true.

Solution 2.

It's also really straightforward reorder XML elements:

 <user id_user="2" login="john" />

 <user id_user="1"/>

Solution 3.

In more complex XML files previous solutions can't be used, so define in first XML element as null. So it should like this:

 <user id_user="1" login ="[NULL]"/>

 <user id_user="2" login="john" />

DbUnit doesn't understand null value as null value. There have to be piece of code that learn DbUnit [NULL] value:

IDataSet dataSet = new FlatXmlDataSet(new FileInputStream("dataset.xml"));
ReplacementDataSet finalDataSet = new ReplacementDataSet(dataSet);
finalDataSet.addReplacementObject("[NULL]", null);

I don't know if it's DbUnit error or some misinterpretation of my XML file but DbUnit detect problem at line 1 or 2 even if real problem occurs many lines after.

Thursday, December 5, 2013

guava Files - unable to delete temporaly directory

I had strange problem on my Mac. Following code:
File indexBaseFile = com.google.common.io.Files.createTempDir();

... some creating of files inside directory

Files.deleteRecursively(indexBaseFile);
always ends with following exception:
java.io.IOException: Failed to delete /var/folders/c5/j25r9j1j1jx_t4wzrzmf53300000gn/T/1386264920188-0
 at com.google.common.io.Files.deleteRecursively(Files.java:537)
The trick is that guava library before deleting file check that deleted file is not a symbolic link. On Mac /var is pointing to /private/var. So now to fix this problem was easy:
File indexBaseFile = com.google.common.io.Files.createTempDir().getAbsolutePath();

... some creating of files inside directory

Files.deleteRecursively(indexBaseFile);
There is extra .getAbsolutePath() calling. This method return absolute path with resolved symbolic links.

Tapestry 5 Invalid configuration

I met following configuration exception in logs:
Caused by: java.lang.IllegalArgumentException: Service 'ChangeLogProcessor' is configured using 
org.apache.tapestry5.ioc.OrderedConfiguration, not org.apache.tapestry5.ioc.Configuration.
 at org.apache.tapestry5.ioc.internal.util.WrongConfigurationTypeGuard.findResource(WrongConfigurationTypeGuard.java:40)
 at org.apache.tapestry5.ioc.internal.util.DelegatingInjectionResources.findResource(DelegatingInjectionResources.java:36)
 at org.apache.tapestry5.ioc.internal.util.DelegatingInjectionResources.findResource(DelegatingInjectionResources.java:38)

Problem was related to Tapestry 5 chain of commands builder. It's Tapestry 5 build-in service that helps to implement that design pattern. My chain of commands definition was:
public static ChangeLogProcessor buildChangeLogProcessor(List<ChangeLogProcessor> commands,
    @InjectService("ChainBuilder") ChainBuilder chainBuilder) {
    return chainBuilder.build(ChangeLogProcessor.class, commands);
}

public static void contributeChangeLogProcessor(
    final Configuration<ChangeLogProcessor> configuration,
    final @Autobuild ChangeLogBoxProcessor changeLogBoxProcessor) {
    configuration.add(changeLogBoxProcessor);
}
After some time I found that problem was in contribute method, in build method is configuration defined as List which should be in contribute method defined as OrderedConfiguration not as Configuration. So following corrected code works fine:
public static void contributeChangeLogProcessor(
    final OrderedConfiguration<ChangeLogProcessor> configuration,
    final @Autobuild ChangeLogBoxProcessor changeLogBoxProcessor) {
    configuration.add("boxProcessor", changeLogBoxProcessor);
}