Problems accessing another jar file from my plugin

Hi all,
I’m developing a Servoy plugin that uses another jar file (jmagick.jar) that uses a native library.
I’m on a Mac with Mac OS X Server 10.4.1 with Servoy Developer R2 2.2 build 328.
The layout is:
myPlugin → jmagick.jar → nativeLibrary.jnilib

The plugin works if I try it from inside XCode, but if I try to use it from a Servoy solution it displays an error in the execution of the Javascript method that calls the plugin method. If I click the Details button it displays java.lang.ExceptionInInitializerError.
If I check the Console log I see:

org.mozilla.javascript.JavaScriptException: java.lang.ExceptionInInitializerError
org.mozilla.javascript.JavaScriptException: java.lang.ExceptionInInitializerError
	at org.mozilla.javascript.JavaScriptException.wrapException(JavaScriptException.java:71)
	at org.mozilla.javascript.NativeJavaMethod.call(NativeJavaMethod.java:366)
	at org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1249)
	at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:2031)
	at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:94)
	at com.servoy.j2db.scripting.e.call(Unknown Source)
	at org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1249)
	at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:2031)
	at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:94)
	at com.servoy.j2db.scripting.e.call(Unknown Source)
	at com.servoy.j2db.develop.debugger.k.a(Unknown Source)
	at com.servoy.j2db.develop.debugger.k.executeFunction(Unknown Source)
	at com.servoy.j2db.FormPanel.a(Unknown Source)
	at com.servoy.j2db.FormPanel.a(Unknown Source)
	at com.servoy.j2db.FormPanel$b.actionPerformed(Unknown Source)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1882)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2202)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:234)
	at java.awt.Component.processMouseEvent(Component.java:5562)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3093)
	at java.awt.Component.processEvent(Component.java:5327)
	at java.awt.Container.processEvent(Container.java:2010)
	at java.awt.Component.dispatchEventImpl(Component.java:4029)
	at java.awt.Container.dispatchEventImpl(Container.java:2068)
	at java.awt.Component.dispatchEvent(Component.java:3877)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4256)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3936)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3866)
	at java.awt.Container.dispatchEventImpl(Container.java:2054)
	at java.awt.Window.dispatchEventImpl(Window.java:1766)
	at java.awt.Component.dispatchEvent(Component.java:3877)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:267)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:196)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:190)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:182)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
java.lang.ExceptionInInitializerError
	at it.melasistemi.servoy.plugin.ImageConverter.toJPG(ImageConverter.java:19)
	at it.melasistemi.servoy.plugin.ServoyPluginProvider.js_createJPGImageFromPNG(ServoyPluginProvider.java:28)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at org.mozilla.javascript.NativeJavaMethod.call(NativeJavaMethod.java:332)
	at org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1249)
	at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:2031)
	at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:94)
	at com.servoy.j2db.scripting.e.call(Unknown Source)
	at org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1249)
	at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:2031)
	at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:94)
	at com.servoy.j2db.scripting.e.call(Unknown Source)
	at com.servoy.j2db.develop.debugger.k.a(Unknown Source)
	at com.servoy.j2db.develop.debugger.k.executeFunction(Unknown Source)
	at com.servoy.j2db.FormPanel.a(Unknown Source)
	at com.servoy.j2db.FormPanel.a(Unknown Source)
	at com.servoy.j2db.FormPanel$b.actionPerformed(Unknown Source)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1882)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2202)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:234)
	at java.awt.Component.processMouseEvent(Component.java:5562)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3093)
	at java.awt.Component.processEvent(Component.java:5327)
	at java.awt.Container.processEvent(Container.java:2010)
	at java.awt.Component.dispatchEventImpl(Component.java:4029)
	at java.awt.Container.dispatchEventImpl(Container.java:2068)
	at java.awt.Component.dispatchEvent(Component.java:3877)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4256)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3936)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3866)
	at java.awt.Container.dispatchEventImpl(Container.java:2054)
	at java.awt.Window.dispatchEventImpl(Window.java:1766)
	at java.awt.Component.dispatchEvent(Component.java:3877)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:267)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:196)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:190)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:182)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
Caused by: java.lang.RuntimeException: Can't load MagickLoader (class not found)
	at magick.Magick.<clinit>(Magick.java:25)
	... 44 more

For what I understand, the error comes from line 25 of Magick.java, a source file of jmagick.jar.

File Magick.java contains:

package magick;


import java.awt.Rectangle;


/**
 * The sole purchase of this class is to cause the native
 * library to be loaded whenever a concrete class is used
 * and provide utility methods.
 *
 * @author Eric Yeo
 * @author Max Kollegov <virtual_max@geocities.com>
 */
public class Magick {

    static {
        String clprop = System.getProperty("jmagick.systemclassloader");
        if (clprop == null || clprop.equalsIgnoreCase("yes")) {
            try {
                ClassLoader.getSystemClassLoader()
                    .loadClass("magick.MagickLoader").newInstance();
            }
            catch(ClassNotFoundException e) {
                throw new RuntimeException("Can't load MagickLoader " + /* LINE 25 */
                                           "(class not found)");
            }
            catch(IllegalAccessException e) {
                throw new RuntimeException("Access to SystemClassLoader "+
                                           "denied (IllegalAccessException)");
            }
            catch(InstantiationException e) {
                throw new RuntimeException("Can't instantiate MagicLoader " +
                                           "(InstantiationException)");
            }
        }
        else {
            System.loadLibrary("JMagick");    
        }
    }

    /**
     * Parses a geometry specification and returns the
     * width, height, x, and y values in the rectangle.
     * It also returns flags that indicates which of the
     * four values (width, height, xoffset, yoffset) were
     * located in the string, and whether the x and y values
     * are negative.  In addition, there are flags to report
     * any meta characters (%, !, <, and >).
     * @param geometry String containing the geometry specifications
     * @param rect The rectangle of values x, y, width and height
     * @return bitmask indicating the values in the geometry string
     * @see magick.GeometryFlags
     */
    public static native int parseImageGeometry(String geometry,
                                                Rectangle rect);

}

At line 25 a RuntimeException is thrown when a ClassNotFoundException is caught.
The ClassNotFoundException is thrown by the ClassLoader.loadClass() method call, because apparently it doesn’t find the MagickLoader class.
But I checked and in jmagick.jar the file MagickLoader.class exists.

I tried to create a JNLP launch file and to place it in the plugin folder, but the error still occurs.
It’s named myPlugin.jar.jnlp and contains:

<?xml version="1.0" encoding="utf-8"?>

<jnlp spec="1.0+"

      codebase="%%serverURL%%"
      href="/plugins/ServoyPlugin.jar.jnlp">
   <information>
      <title>Servoy Thumbnail Generator</title>
      <vendor>Mela Sistemi</vendor>
      <offline-allowed/>
   </information>
   <resources>
      <jar href="/lib/jmagick.jar" download="eager"/>
   </resources>
   <component-desc/>

</jnlp>

Obviously I placed jmagick.jar inside the lib folder of Servoy.

Any idea of what’s happening? :?

I found that if I place jmagick.jar in /Library/Java/Extensions/ it works, indeed I get another error, related to the next step in the chain.
Out of curiosity, how should I do to place my additional jar file wherever I want?

Well the magic people seem they do not anticipate on the fact there class is not loaded by system classloader (or they do in strange way by requiring the jmagick.systemclassloader=false in system properties)

So you could do:

  1. System.setProperty(“jmagick.systemclassloader”,“false”);

  2. or modify magic by doing
    Magic.class.getClassLoader() .loadClass(“magick.MagickLoader”).newInstance();

Yes, I tried the first solution and it works, thanks Jan! :smiley: