# Issue with Third Party Dependent Libraries in Custom Module

**URL:** <https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195>\
**Category:** Module Development\
**Created:** [October 13, 2024, 7:16pm UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195 "2024-10-13T19:16:03Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Markus\_Oosthuizen](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/markus_oosthuizen/32/81030_2.png) [@Markus\_Oosthuizen](https://forum.inductiveautomation.com/u/Markus_Oosthuizen)\
**Post date:** [October 13, 2024, 7:16pm UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/1 "2024-10-13T19:16:03Z")

</div>

Hi everyone,

I'm currently developing a module, and I'm encountering an issue where the module isn't recognizing the dependent libraries specified in my `build.gradle` file. These libraries are included in the common scope, and the `module.xml` correctly shows them with the right scope. I've used `modlApi`, and I’ve confirmed that the dependencies are bundled in the `.modl` file.

However, the module fails to locate the libraries during runtime, and the only workaround that seems to fix the issue is manually copying the JARs into the `lib/core/gateway` folder and restarting the Ignition Gateway. After doing this, the module works as expected with no errors when the script runs.

Am I missing something obvious here? Is there a better or programmatic way to ensure that these dependencies are added to the classpath and library path when the module is installed?

Any guidance or suggestions would be greatly appreciated!

Thanks in advance!

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [October 13, 2024, 7:33pm UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/2 "2024-10-13T19:33:51Z")

</div>

If the dependencies are listed in the module.xml and part of the .modl file that's a good start... assuming that's all correct - what does the error look like? How are you determining these dependencies are or are not on the class path?

---

<div class="post-metadata">

**Author:** ![Markus\_Oosthuizen](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/markus_oosthuizen/32/81030_2.png) [@Markus\_Oosthuizen](https://forum.inductiveautomation.com/u/Markus_Oosthuizen)\
**Post date:** [October 13, 2024, 8:23pm UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/3 "2024-10-13T20:23:09Z")

</div>

So at the moment I get the output from "System.getProperty("java.class.path")". I have even tried to dynamically copy over my .jar files to the lib/core/gateway directory and add them to the "java.class.path" property, but still, it would only work after a gateway restart.

The error:

```python
com.inductiveautomation.ignition.common.script.JythonExecException: Traceback (most recent call last): File "", line 11, in runAction File "", line 2, in faceDetection at ai.djl.repository.zoo.Criteria.loadModel(Criteria.java:125) at com.lidttech.deeplearning.facerecognition.FaceDetection.predictFaceDetection(FaceDetection.java:215) at com.lidttech.deeplearning.facerecognition.FaceDetection.runDetection(FaceDetection.java:60) at com.lidttech.deeplearning.facerecognition.FaceRecognitionGatewayScriptModule.runFaceDetectionImpl(FaceRecognitionGatewayScriptModule.java:28) at com.lidttech.deeplearning.facerecognition.DeepLearningModule.runFaceDetection(DeepLearningModule.java:43) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) ai.djl.repository.zoo.ModelNotFoundException: ai.djl.repository.zoo.ModelNotFoundException: ModelZoo doesn't support specified engine: PyTorch

at org.python.core.Py.JavaError(Py.java:545)

at org.python.core.Py.JavaError(Py.java:536)

at org.python.core.PyReflectedFunction. __call__ (PyReflectedFunction.java:192)

at com.inductiveautomation.ignition.common.script.ScriptManager$ReflectedInstanceFunction. __call__ (ScriptManager.java:553)

at org.python.core.PyObject. __call__ (PyObject.java:494)

at org.python.core.PyObject. __call__ (PyObject.java:498)

at org.python.pycode._pyx7.faceDetection$1(:2)

at org.python.pycode._pyx7.call_function()

at org.python.core.PyTableCode.call(PyTableCode.java:173)

at org.python.core.PyBaseCode.call(PyBaseCode.java:168)

at org.python.core.PyFunction. __call__ (PyFunction.java:437)

at org.python.pycode._pyx6.runAction$1(:20)

at org.python.pycode._pyx6.call_function()

at org.python.core.PyTableCode.call(PyTableCode.java:173)

at org.python.core.PyBaseCode.call(PyBaseCode.java:306)

at org.python.core.PyFunction.function ___call__ (PyFunction.java:474)

at org.python.core.PyFunction. __call__ (PyFunction.java:469)

at org.python.core.PyFunction. __call__ (PyFunction.java:464)

at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:847)

at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:829)

at com.inductiveautomation.ignition.gateway.project.ProjectScriptLifecycle$TrackingProjectScriptManager.runFunction(ProjectScriptLifecycle.java:868)

at com.inductiveautomation.ignition.common.script.ScriptManager$ScriptFunctionImpl.invoke(ScriptManager.java:1010)

at com.inductiveautomation.ignition.gateway.project.ProjectScriptLifecycle$AutoRecompilingScriptFunction.invoke(ProjectScriptLifecycle.java:950)

at com.inductiveautomation.perspective.gateway.script.ScriptFunctionHelper.invoke(ScriptFunctionHelper.java:161)

at com.inductiveautomation.perspective.gateway.script.ScriptFunctionHelper.invoke(ScriptFunctionHelper.java:98)

at com.inductiveautomation.perspective.gateway.action.ScriptAction.runAction(ScriptAction.java:80)

at com.inductiveautomation.perspective.gateway.model.ActionCollection$ActionSequence$ExecuteActionsTask.lambda$call$0(ActionCollection.java:263)

at com.inductiveautomation.perspective.gateway.api.LoggingContext.mdc(LoggingContext.java:54)

at com.inductiveautomation.perspective.gateway.model.ActionCollection$ActionSequence$ExecuteActionsTask.call(ActionCollection.java:252)

at com.inductiveautomation.perspective.gateway.model.ActionCollection$ActionSequence$ExecuteActionsTask.call(ActionCollection.java:221)

at com.inductiveautomation.perspective.gateway.threading.BlockingTaskQueue$TaskWrapper.run(BlockingTaskQueue.java:154)

at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)

at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

at com.inductiveautomation.perspective.gateway.threading.BlockingWork$BlockingWorkRunnable.run(BlockingWork.java:58)

at java.base/java.lang.Thread.run(Unknown Source)

Caused by: org.python.core.PyException: ai.djl.repository.zoo.ModelNotFoundException: ai.djl.repository.zoo.ModelNotFoundException: ModelZoo doesn't support specified engine: PyTorch

... 37 common frames omitted

Caused by: ai.djl.repository.zoo.ModelNotFoundException: ModelZoo doesn't support specified engine: PyTorch

at ai.djl.repository.zoo.Criteria.loadModel(Criteria.java:125)

at com.lidttech.deeplearning.facerecognition.FaceDetection.predictFaceDetection(FaceDetection.java:215)

at com.lidttech.deeplearning.facerecognition.FaceDetection.runDetection(FaceDetection.java:60)

at com.lidttech.deeplearning.facerecognition.FaceRecognitionGatewayScriptModule.runFaceDetectionImpl(FaceRecognitionGatewayScriptModule.java:28)

at com.lidttech.deeplearning.facerecognition.DeepLearningModule.runFaceDetection(DeepLearningModule.java:43)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

at java.base/java.lang.reflect.Method.invoke(Unknown Source)

at org.python.core.PyReflectedFunction. __call__ (PyReflectedFunction.java:190)

```

So it actually says :"Caused by: ai.djl.repository.zoo.ModelNotFoundException: ModelZoo doesn't support specified engine: PyTorch", but it gives this when it can't find the model class.

At the moment I have my own directory located inside the lib directory and I add my directory to the library and classpath inside the Ignition.conf file.

Still, I need a gateway restart after installing the module.

Any ideas?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [October 13, 2024, 10:39pm UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/4 "2024-10-13T22:39:32Z")

</div>

Looks like a side effect of modules having isolated classloaders.

If the library does dynamic classloading, a Perspective thread won't be using the right classloader for the library guts to get what they need. Modules really need to expose everything necessary for scripting in `system.something.*`, and those exposed functions should temporarily switch their thread classloader to the module's.

---

<div class="post-metadata">

**Author:** ![Markus\_Oosthuizen](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/markus_oosthuizen/32/81030_2.png) [@Markus\_Oosthuizen](https://forum.inductiveautomation.com/u/Markus_Oosthuizen)\
**Post date:** [October 14, 2024, 8:05am UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/5 "2024-10-14T08:05:43Z")

</div>

I am actually using the system.somefunction.\* structure to do the scripting. Still no luck. I am going to try do a fresh install of Ignition on a new OS and see if it gives the same issue.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [October 14, 2024, 11:39am UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/6 "2024-10-14T11:39:53Z")

</div>

> [@Markus\_Oosthuizen](#):
>
> gives the same issue.

I would be surprised if it didn't.

Are you changing classloaders while running your exposed scripting functions?

---

<div class="post-metadata">

**Author:** ![Markus\_Oosthuizen](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/markus_oosthuizen/32/81030_2.png) [@Markus\_Oosthuizen](https://forum.inductiveautomation.com/u/Markus_Oosthuizen)\
**Post date:** [October 14, 2024, 12:17pm UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/7 "2024-10-14T12:17:42Z")

</div>

Uhm... Not sure to be honest, I don't think so. Any guidance here on best practice would be appreciated. (Not a frequent Iava developer, actually only learnt it for module development)

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [October 14, 2024, 12:28pm UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/8 "2024-10-14T12:28:35Z")

</div>

There's not so much a best practice, as "necessary" practice. Most libraries don't need any special classloader manipulation, as they make static references to all the other classes they use.

Libraries that load classes by string name instead of a static reference typically rely on the thread's current classloader, deliberately, so that the caller can control where the named class comes from. Your call trace suggests that is what is happening.

Your exposed script function(s) having this problem need to capture the calling thread's classloader into a local variable, then change to the module's classloader (simply grab the gateway hook's class's classloader), then in a `finally` clause, restore the caller's classloader.

---

<div class="post-metadata">

**Author:** ![Markus\_Oosthuizen](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/markus_oosthuizen/32/81030_2.png) [@Markus\_Oosthuizen](https://forum.inductiveautomation.com/u/Markus_Oosthuizen)\
**Post date:** [October 14, 2024, 1:54pm UTC](https://forum.inductiveautomation.com/t/issue-with-third-party-dependent-libraries-in-custom-module/97195/9 "2024-10-14T13:54:40Z")

</div>

Wow!! That solved it!! Thank you so much I added as you said and worked first time:

```python
// Capture the current thread's classloader
        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();

        // Switch to the module's classloader
        ClassLoader moduleClassLoader = FaceDetection.class.getClassLoader();

        try {
            Thread.currentThread().setContextClassLoader(moduleClassLoader);
            // Proceed with the detection process
            if (inputType.equalsIgnoreCase("Image")) {
                Image img = (Image) setImage(inputSource);
                if (img != null) {
                    DetectedObjects detection = predictFaceDetection(img);
                    logger.info("Detected Objects: {}", detection);
                    return handleOutput(img, detection, outputType);
                }
            } else if (inputType.equalsIgnoreCase("Video")) {
                processVideoStream((String) inputSource, outputType);
            }
        } finally {
            // Restore the original classloader
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }

```

Appreciate all the help.
