I couldn't persuade JMagick (Java bindings for ImageMagick) to run on Tomcat. The problem was fairly obviously related to loading the DLL, as I was getting the following (highly abbreviated) stack trace:
...whopping great stack trace... Caused by: java.lang.RuntimeException: Can't load MagickLoader (class not found) at magick.Magick.(Magick.java:25) ... 66 more
The MagickLoader class's implementation (in its entirety) reads thusly:
package magick; /** * The sole purpouse of this class is to cause the native * library to be loaded in SYSTEM classloader whenever a * concrete class is used. * * @author Max Kollegov <Virtual_Max@geocities.com> */ public class MagickLoader { static { System.loadLibrary("JMagick"); } }
Given that rather terse implementation, any problem pretty much HAS to be a matter of finding the library. Normally that would mean that the java.library.path wasn't set up correctly, but I'd triple checked that:
- The JMagick.DLL library was on the System PATH
- The Library loaded fine from a stand-alone Java test app.
- A test JSP reported the java.library.path as containing the path to the JMagick.DLL library's directory.
After a lot of flailing around I eventually found this post that mentions an additional property to set when working with Tomcat: http://www.fudgemonday.com/entry/20070409:
Set the jmagick.systemclassloader property to no and everything springs into life. On Windows the easiest way to achieve this is to add an entry reading -Djmagick.systemclassloader=no to the Java Options on the Java tab of the Tomcat monitor ("Apache Tomcat Properties") systray application.
Nice. The purpose of my post is basically to correlate the error message with that fix for it so that you don't have to waste as much time looking for this flag as I did! I'll update FAQ-O-Matic on the JMagick site with this factoid too. D'oh, it's not available for public updates!
Incidentally, the reason this flag fixes things is this: JMagick uses another class ("Magick") to reflectively load the MagickLoader class in order to ensure that the DLL is loaded. But by default it loads MagickLoader using the System classloader (I don't know why). In a Tomcat environment the system classloader doesn't know about classes in the WEB-INF/lib directory of your web application - they're owned by a different classloader), so Magick is unable to locate the MagickLoader class in the system classloader. Setting this flag causes Magick to call System.loadLibrary directly instead of delegating to MagickLoader.