I would like to make a ResourceAccessor (2.0) that can interpret Strings as URLs.

(Or perhaps one already exists somewhere?)

I am not clear on the (undocumented) ResourceAccessor/FileOpener contract.


What does toClassLoader() do?  I could see that for a ClassLoaderResourceAccessor it might simply return something like the Thread.currentThread().getContextClassLoader(), but what should it return in my case, where I am not going to store any persistent state in member variables?  What is the return value used for?

What does getResources(packageName) do?  I assume this is to abstract away something related to ClassLoader.getResources, but again, when is it used, and what is the return value good for?  Is packageName always truly a package name, or is it just an arbitrary string?  Can it be null?

(Finally, the larger use case is that I’d like to dynamically aggregate a bunch of changelog fragments together.  I’m deliberately punting on the topological sorting issue, and am relying on the fact that due to my project organization it is highly, highly unlikely that any two fragments will depend on one another.  So I’m planning on starting out by doing ClassLoader.getResources("/META-INF/liquibase/changelog.xml"), and then, armed with a pile of URLs originating in various jar files around the classpath, I will either make a new DatabaseChangeLog that is the result of programmatically reading all these other DatabaseChangeLogs in, or create a temp file that does the inclusion for me.)


For anyone following along, I did this (against 1.9.5; can’t wait for 2.0):

I made a URLResourceAccessor that extends ClassLoaderFileOpener.

I overrode only the getResourceAsStream(String) method to do this:
new URL(name).openStream();

Then, obviously, when I create a new Liquibase object from within my JUnit test class, I have to make sure that the physical location of the change log file is actually in URL form, and then I pass my URLResourceAccessor.  This means that the aggregating changelog file will be opened as a URL, and any include statements inside will be opened as URLs.  I have no idea what the getResources() or toClassloader() methods are being used for, so, like I said, I just let them flow through to the classloader implementation, which seemed safe enough.

Hack?  You bet.  Works?  Yep.


You are right that the job of the FileOpeners/ResourceAccessors are to abstract how liquibase accesses resources.  The toClassLoader() is used to generate a classloader object that can be used to load classes from your store.  It’s used to find and load custom change classes.  The getResourceAsStream method is used to read file contents, in particular changeLog files.  The getResources() method is used to list the resources that are available in a package for later reading by the getResourceAsStream method.

I do plan to document it all for the 2.0 final release.

There is a CompositeResourceAccessor which can be used to aggregate multiple ResourceAccessors together, but if you have a particular order needed, you may need to add custom logic.