Path to XML DTD for DBUnit in multi-module Java/Maven project?
Asked Answered
F

5

9

I have a multi-module maven project. Within the persist module I have a number of XML files data files that reference a DTD:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE myapp-data SYSTEM "myapp-data.dtd" >

<dataset>
      .....omitted for brevity....
</dataset>

The DTD is stored in the same directory with the XML files and even Eclipse reports these XML files as valid.

However, when I run the application, the DBUnit FlatXMLDataSet throws a FileNotFound exception because it cannot located the DTD. It is apparently looking for the DTD in the root project directory (e.g. myproject/). I would have expected it to look for the DTD in the same directory as the XML file itself (e.g. myproject/persist/target/test-data).

Looking at the DBUnit source code, it has this to say about it "Relative DOCTYPE uri are resolved from the current working dicrectory."

What's a good way to fix this?

Fisticuffs answered 10/6, 2010 at 19:37 Comment(0)
F
9

OK, I think I figured this one out. Thank goodness for open source.

There is a method on FlatXmlDataSetBuilder that takes a stream to the DTD. It's crazy that this is a public method IMO, but then again, its crazy that DBUnit doesn't look in the same directory as the XML for the dtd file. So here it is:

String dtdResourceName = "classpath:test-data/myapp-data.dtd";      
Resource res = applicationContext.getResource(dtdResourceName);
builder.setMetaDataSetFromDtd(res.getInputStream());

Now I leave the DOCTYPE declaration with the dtd in the same directory as the XML and use this hack to fool DBUnit into doing the Right Thing.

Fisticuffs answered 13/6, 2010 at 2:46 Comment(2)
what version are you using? I can't find this code here: dbunit.sourceforge.net/xref/org/dbunit/dataset/xml/…Shinbone
It's in the link you provided...check line 195 -- hard to find actually. dbunit.sourceforge.net/xref/org/dbunit/dataset/xml/…Fisticuffs
S
3

Always use the correct variables to access special directories, because multi-module builds have a different working directory than local builds:

So

  • instead of mydir use ${project.basedir}/mydir
  • instead of target/mydir use ${project.build.directory}/mydir
  • instead of target/classes/mydir use ${project.build.outputDirectory}/mydir

These variables always evaluate to the current project, no matter where it is called from. Here is an Overview of POM variables (not complete but the most important stuff is in there)

Also, if you ever want to do some interactive query-style debugging, the help:evaluate mojo comes in handy:

just call

mvn help:evaluate

and you will be prompted for an expression. If you enter an expression e.g. ${project.build.plugins[0]} , the merged dom for the specified element will be listed


EDIT:

ok, now I think I see the problem. then why not just reference the directory in the xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE myapp-data SYSTEM "target/test-classes/myapp-data.dtd" >

I know it's not pretty, but it should work, multi-module or not. the current directory for unit tests is always the current ${project.basedir}, not the parent project dir.

Shinbone answered 11/6, 2010 at 7:34 Comment(3)
I don't think the problem is with Maven, its with DBUnit. Its looking in the wrong place for the DTD. Is there another better way to specify that within the XML file? Is there a DBUnit setting I am missing?Fisticuffs
Doing the new approach solves the runtime problem just fine -- but now Eclipse is complaining that it can't locate the DTD for validation. I wanna have my cake and eat it too!!Fisticuffs
The "target" directory only exists after a maven build. If I run mvn clean it is not there and then Eclipse goes back to complaining! I've examined the source code to DBUnit and they really go out of their way to NOT find the dtd....Fisticuffs
T
1

You could publish the DTD to a web server and then put its HTTP URL into the DOCTYPE, e.g.:

<!DOCTYPE myapp-data SYSTEM "-//The Owner//The Description//EN" "http://host/path/to/myapp-data.dtd">
Trinatte answered 18/9, 2013 at 0:32 Comment(0)
I
0

Try using "File" instead of "FileInputStream" when opening an XML file.

For example:

ReplacementDataSet dataSet = new ReplacementDataSet(new FlatXmlDataSet(new File(fileName)));

This way, relative path to DTD should start with directory of the XML file.

And if you use

ReplacementDataSet dataSet = new ReplacementDataSet(new FlatXmlDataSet(new FileInputStream(fileName)));

path should be relative to current working directory.

Ibnsina answered 15/2, 2012 at 19:24 Comment(0)
T
0

It involves some ugly duplication, but you could paste the contents of the DTD into the XML file(s) in question and then use them as internal DTDs.

Trinatte answered 18/9, 2013 at 0:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.