2

in my Java project I am using an H2 in-memory database, for which I have to load the JDBC driver when I initialize my application. I want/need to load the H2 .jar file dynamically, so I do the following:

String classname = "org.h2.Driver";
URL u = new URL("jar:file:libs/h2.jar!/");
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Driver d = (Driver) Class.forName(classname, true, ucl).newInstance();
DriverManager.registerDriver(new DriverShim(d));

When I put the H2 .jar file into a "libs" folder outside my Java source code folder (that is, in Eclipse, this "libs" directory is on the same level as the "src" folder), then this approach works fine. However, unfortunately I have to put this H2 .jar file into a folder within the source code folder tree, but below the main class folder.

For example, my Java package structure looks like this in Eclipse:

<project>/src/my/app/MyApp.java              // main class of my application
<project>/src/my/app/sub/package/h2.jar      // how to access this?
<project>/libs/h2.jar                        // loading from here works

I know this is stupid, but unfortunately I have to work with this strange setup. But what I don't know: how can I edit my Java code (listed above) in order to work with this setup?

EDIT: This has to work outside Eclipse as well, so adding the JAR file to the Java Build Path in Eclipse is no option for me.

EDIT2: I already tried to load "jar:file:my/app/sub/package/h2.jar!/", but that did not work for me.

Thanks in advance for all helpful ideas!

Kind regards, Matthias

Matthias
  • 9,817
  • 14
  • 66
  • 125
  • Did you try jar:file:my/app/sub/package/h2.jar? – kosa May 10 '12 at 20:40
  • The "strange setup" you describe is a pretty normal directory structure. There's no reason to put JAR files in your `src` folder. It sounds like you simply don't have `h2.jar` on your classpath. – matt b May 10 '12 at 20:44
  • @thinksteep: yes, tried that, but it did not work. – Matthias May 10 '12 at 20:44
  • @matt b: I know that there is no reason to put the JAR file into the src folder -- but as said, I have to live with it, I can't change that requirement. – Matthias May 10 '12 at 20:45
  • Why do you have that kind of a requirement? – Esko Luontola May 10 '12 at 21:15
  • @Esko: Because it's kind of an automatic, online build system, where to I can only upload files and folders below my/app/* – Matthias May 10 '12 at 21:19
  • Online build system? Why not build locally and upload the JARs? Is this some homework which requires the files to be uploaded to a system which checks that they work as specified in the assignment? – Esko Luontola May 10 '12 at 21:25
  • No it's not a homework, but an online build system that uses my classes under my/app/* and several other classes (not from me) to automatically build the whole solution. Anyway, I can't give you more details on the internals of this system, as I don't know them. As said, I simply have to live with it, and that is why I am asking here... – Matthias May 10 '12 at 21:43
  • 1
    Now that's a weird build system - needlessly complicating the life of developers. You mean this is commercial work? How about submitting your story to http://thedailywtf.com/ ;) – Esko Luontola May 10 '12 at 21:45

1 Answers1

2

In some frameworks referring to files inside JARs can be done using the classpath: prefix. I doubt URLClassLoader supports it natively, but it's worth a try (e.g. classpath:/my/app/sub/package/h2.jar). But since that doesn't work with URLClassLoader, here are other ways:

One way to do it would be to write your own ClassLoader which reads the JAR file from classpath (using getResourceAsStream), uncompresses it (using ZipInputStream) to memory (e.g. a map of byte arrays) and loads the classes from there.

Another, slightly easier way, is to read the JAR file from classpath and write it into a temporary file. Then you can use the plain URLClassLoader to load classes from it. This has the disadvantage that the file must be written to a file and the file probably cannot be removed until the JVM exits (unless using Java 7 or higher).

I'm using the second approach (copying to a temp file) in one project, though I'm using it to launch an external process. I would be curious to hear why you have such a requirement. If it's just a matter of having the whole application in one JAR, there are numerous simpler methods for achieving that (Maven Assembly Plugin, Maven Shade Plugin, Jar Jar Links, One-JAR to name a few).


No it's not a homework, but an online build system that uses my classes under my/app/* and several other classes (not from me) to automatically build the whole solution. Anyway, I can't give you more details on the internals of this system, as I don't know them. As said, I simply have to live with it, and that is why I am asking here...

Sounds like you are working in a WTF environment (does it have a name?), so here are some ways to start hacking around it:

Find out more about your environment, especially absolute file paths of the following: directory where the source files are saved, directory where the generated .class files are saved, and the current working directory when the program is run.

If you can get any kind of output of what your program prints during runtime, you can put into your application some debug code where you use File.listFiles() to crawl the machine's directory trees. If you can get output only from what happens when compiling, it might be possible to execute your own code during compile by creating your own annotation processor (apt is part of javac since Java 6), though I'm not sure whether the annotation processor must be compiled first separately.

The working directory can be read from the user.dir system property and the location of class files can be probably gotten from the java.class.path system property (unless custom class loaders are used). There is no guarantee that a JAR file in the source directory would be copied to the classpath, so you might need to do some looking around.

Then when you know the file path of the JAR file, then you can get an URL to it using new File("path/to/h2.jar").toURI().toURL() which you can then pass to URLClassLoader.

If nothing else works, upload the source code of the libraries and compile them together with your project.

In the long run, try to replace the WTF build environment with one that uses a standard build tool (such as Maven) and a common CI server (such as Jenkins). It's normal for projects to have lots of library dependencies, so you shouldn't need to hack around a build environment to use them.

Esko Luontola
  • 73,184
  • 17
  • 117
  • 128
  • Unfortunately, using "classpath:" does not work. I have to work with this environemtn, because it's kind of an automatic, online build system, where to I can only upload files and folders below my/app/* – Matthias May 10 '12 at 21:21
  • I've updated my answer. Can you find out more about the environment using these tips? Is the environment public, so that we could have a look at it? – Esko Luontola May 10 '12 at 22:19
  • I guess you're right, that's really a WTF environment. I will try to get into contact with its owner and find out the directories in which all the class files would end up. Also, maybe he can manually copy the .jar file into an appropriate directory... – Matthias May 10 '12 at 22:30