3rd party library not available in Gateway scope

I wrote a small module that used Apache PDFBox.
I began running into the error message in the gateway logs:

	Cannot read JPEG2000 image: Java Advanced Imaging (JAI) Image I/O Tools are not installed

Googling led me to updating my common/pom.xml to include the dependency libraries.
This resolved this issue when I tested via Designer's script console.

However when the exact same script is run in gateway scope off a tag event script, the error resurfaces. I copied the dependency jars directly into lib/core/gateway and the same module began working properly. Am I missing something in trying to get the dependency jars loaded for gateway execution?

Here is my module.xml

<?xml version="1.0" encoding="UTF-8"?>
		<description>Artek created helpful utilities for Ignition.</description>
		<jar scope="G">commons-logging-1.2.jar</jar>
		<jar scope="G">jai-imageio-core-1.4.0.jar</jar>
		<jar scope="G">Artek-Utilities-common-1.0.jar</jar>
		<jar scope="G">jbig2-imageio-3.0.4.jar</jar>
		<jar scope="G">jai-imageio-jpeg2000-1.4.0.jar</jar>
		<jar scope="G">Artek-Utilities-gateway-1.0.jar</jar>
		<jar scope="G">fontbox-2.0.27.jar</jar>
		<jar scope="G">pdfbox-2.0.27.jar</jar>
		<jar scope="CD">commons-logging-1.2.jar</jar>
		<jar scope="CD">Artek-Utilities-client-1.0.jar</jar>
		<jar scope="CD">jai-imageio-core-1.4.0.jar</jar>
		<jar scope="CD">Artek-Utilities-common-1.0.jar</jar>
		<jar scope="CD">jbig2-imageio-3.0.4.jar</jar>
		<jar scope="CD">Artek-Utilities-designer-1.0.jar</jar>
		<jar scope="CD">jai-imageio-jpeg2000-1.4.0.jar</jar>
		<jar scope="CD">fontbox-2.0.27.jar</jar>
		<jar scope="CD">pdfbox-2.0.27.jar</jar>
		<hook scope="C">com.Artek.client.ClientHook</hook>
		<hook scope="D">com.Artek.designer.DesignerHook</hook>
		<hook scope="G">com.Artek.GatewayHook</hook>

Your module dependencies are not available as imports for gateway-scoped scripting. Modules get loaded in their own ClassLoader, underneath the platform ClassLoader, and a gateway-scoped scripting has no access to these classes.

If your module adds scripting functions that in turn use these dependencies that will work.


Ah that makes sense. I'll make a note to copy the jars into lib/core/gateway then.

This is a terrible practice in production. If you don't want to provide actual scripting functions, consider using the scripting hooks to provide the classes themselves. The "scripting functions" you register with the ScriptManager would have constructs like this:

static public final Class<?> SomeClassName = org.whatever.path.to.SomeClassName.class;

Then your scripts would use system.Artek.SomeClassName instead of importing from the jars directly. This would work in all scopes, and obeys all module lifecycle expectations.

If that is still too much trouble, consider using the <export scope="G">path/to/some.jar</export> construct in your module.xml file instead of the normal <jar scope="G">....</jar> construct. This will place the jars in the top level class loader. However, once there, changes to the module will not remove or replace them until gateway restart. Still better than manually copying jars.


If I'm understanding correctly- if I were using the additional libraries directly I would naturally expose them in my module class. In this case PDFBox's functions are calling the additional libraries, and without rewriting the functionality of PDFBox the first option would be more difficult to do.

Exporting the jar seems to be the best alternative then. I assume updating the module.xml would have to be a manual step done after the module file is built?

No need to rewrite. The PDFBox jar is still in the same classloader with the rest of your module when you expose classes via scripting. You just need to expose the classes that your scripting actually needs to access and everything else will be fine.

1 Like

There's probably something in the maven plugin to do this. I don't use maven, myself. Sorry.

{ If you make the changes manually, it needs to be before signing. }

I'm not sure if the Maven plugin supports the export stuff. It was/is kind of semi-private functionality. I don't think we even use it any more, and wouldn't expect it to survive into 8.3.

That will be very disappointing, as my Life Cycle module absolutely depends on it. May be moot if you actually manage to get rid of module hot-loading, but I'm hoping you don't. (System startup time is horrible for large systems, and module restarts are extremely useful to solve certain kinds of bugs with minimum downtime.)

This is still in the "happening" column, unless something changes.

Hm I think I've gotten a bit closer with your suggestions.
I added the following into my module class

    static public final Class<?> J2KImageWriteParam =  com.github.jaiimageio.jpeg2000.J2KImageWriteParam.class;
    static public final Class<?> J2KImageReadParam =  com.github.jaiimageio.jpeg2000.J2KImageReadParam.class;
    static public final Class<?> ImageIO1 =  javax.imageio.ImageIO.class;
    static public final Class<?> ImageReader =  javax.imageio.ImageReader.class;
    static public final Class<?> J2KImageReader = com.github.jaiimageio.jpeg2000.impl.J2KImageReader.class;
    static public final Class<?> ImageReaderSpi = javax.imageio.spi.ImageReaderSpi.class;

Digging through the pdfbox jar- it looks like this method is throwing the exception for the class not being found:

            iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
                                    new ContainsFilter(readerFormatNamesMethod,

I'm thinking the classes I've referenced aren't the ones that method is looking for?

Hmmm. ImageReaderSpi should be in the base classloader already, I would think. So too with ImageIO and ImageReader. Don't load those yourself.