# Including Third-Party Dependencies in Ignition Module

**URL:** <https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224>\
**Category:** Module Development\
**Tags:** perspective\
**Created:** [April 17, 2025, 1:08pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224 "2025-04-17T13:08:56Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![sebastian.fekete](https://avatars.discourse-cdn.com/v4/letter/s/977dab/32.png) [@sebastian.fekete](https://forum.inductiveautomation.com/u/sebastian.fekete)\
**Post date:** [April 17, 2025, 1:08pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/1 "2025-04-17T13:08:56Z")

</div>

Hi everyone,

I'm trying to include third-party dependencies in my Ignition module. I attempted this using the perspective-component from the Ignition SDK GitHub by adding the dependencies I need to the `build.gradle.kts` file in the common scope but it didn't import the dependencies correctly.

Could anyone provide examples or suggest convenient ways to do this? Thanks in advance for your help!  
@GeraldB

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [April 17, 2025, 3:28pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/2 "2025-04-17T15:28:12Z")

</div>

Can you post what you’ve currently got in your `build.gradle.kts`?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 17, 2025, 3:29pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/3 "2025-04-17T15:29:39Z")

</div>

What did you actually try, and what was the actual result?

Note that you need to use our explicit `modlApi` dependency marker to tell the module plugin to actually include your dependency in the final output module. `implementation` just tells Gradle to use it for compilation, assuming that you will have it already in your final destination.

---

<div class="post-metadata">

**Author:** ![sebastian.fekete](https://avatars.discourse-cdn.com/v4/letter/s/977dab/32.png) [@sebastian.fekete](https://forum.inductiveautomation.com/u/sebastian.fekete)\
**Post date:** [April 18, 2025, 9:43am UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/4 "2025-04-18T09:43:23Z")

</div>

Thanks for the answers, loading the dependencies with implementation is exactly what i tried that's why it didn't include them in the .modl file. I now tried to use modlApi like this:

```python
plugins {
    `java-library`
}

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(17))
    }
}

repositories {
    mavenCentral()
    maven {
        url = uri("https://company/com/maven2")
    }
    mavenLocal()
    flatDir {
        dirs("libs")
    }
}

dependencies {
    // compileOnly is the gradle equivalent to "provided" scope. Here we resolve the dependencies via the
    // declarations in the gradle/libs.versions.toml file
    compileOnly(libs.ignition.common)
    compileOnly(libs.ignition.perspective.common)
    compileOnly(libs.google.guava)
    compileOnly(libs.ia.gson)

    // https://mvnrepository.com/artifact/io.netty/netty-transport-native-epoll
    modlApi("io.netty:netty-transport-native-epoll:4.2.0.Final")
    // https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common
    modlApi("org.apache.hadoop:hadoop-common:3.4.1")
    // https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-client
    modlApi("org.apache.hadoop:hadoop-client:3.4.1")
    // https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs
    modlApi("org.apache.hadoop:hadoop-hdfs:3.4.1")
}

```

Now it correctly includes the hadoop jar files in the .modl.

when i install the module to my Gateway i get the following error:

```python
ModuleManager	18Apr2025 11:30:35	Error starting module org.fakester.radcomponent
java.lang.NoClassDefFoundError: com/inductiveautomation/perspective/gateway/api/PerspectiveContext

at org.fakester.gateway.RadGatewayHook.startup(RadGatewayHook.java:39)

at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl$LoadedModule.startup(ModuleManagerImpl.java:2446)

at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.startupModule(ModuleManagerImpl.java:1188)

at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl$2.call(ModuleManagerImpl.java:734)

at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.executeModuleOperation(ModuleManagerImpl.java:913)

at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.installModuleInternal(ModuleManagerImpl.java:700)

at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl$InstallCommand.doExecute(ModuleManagerImpl.java:1915)

at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl$AbstractModuleCommand.execute(ModuleManagerImpl.java:1864)

at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl$Receiver.receiveCall(ModuleManagerImpl.java:1820)

at com.inductiveautomation.ignition.gateway.redundancy.QueueableMessageReceiver.receiveCall(QueueableMessageReceiver.java:47)

at com.inductiveautomation.ignition.gateway.redundancy.RedundancyManagerImpl.dispatchMessage(RedundancyManagerImpl.java:1044)

at com.inductiveautomation.ignition.gateway.redundancy.RedundancyManagerImpl$ExecuteTask.run(RedundancyManagerImpl.java:1112)

at com.inductiveautomation.ignition.common.execution.impl.BasicExecutionEngine$ThrowableCatchingRunnable.run(BasicExecutionEngine.java:550)

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.ScheduledThreadPoolExecutor$ScheduledFutureTask.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 java.base/java.lang.Thread.run(Unknown Source)

Caused by: java.lang.ClassNotFoundException: com.inductiveautomation.perspective.gateway.api.PerspectiveContext

at java.base/java.net.URLClassLoader.findClass(Unknown Source)

at com.inductiveautomation.ignition.gateway.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:37)

at java.base/java.lang.ClassLoader.loadClass(Unknown Source)

at com.inductiveautomation.ignition.gateway.modules.ModuleClassLoader.loadClass(ModuleClassLoader.java:85)

at java.base/java.lang.ClassLoader.loadClass(Unknown Source)

... 19 common frames omitted

```

what i don't really understand because i didn't change anything else than the build.gradle.ks in my common scope as shown above

---

<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:** [April 18, 2025, 12:07pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/5 "2025-04-18T12:07:21Z")

</div>

It looks like you're missing the configuration stating your module depends on the Perspective module.

> <https://github.com/inductiveautomation/ignition-sdk-examples/blob/aafc6c3ce2c93841f2856c8e0124c7cec33167ea/perspective-component/build.gradle.kts#L28-L31>

---

<div class="post-metadata">

**Author:** ![sebastian.fekete](https://avatars.discourse-cdn.com/v4/letter/s/977dab/32.png) [@sebastian.fekete](https://forum.inductiveautomation.com/u/sebastian.fekete)\
**Post date:** [April 18, 2025, 4:51pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/6 "2025-04-18T16:51:06Z")

</div>

I just checked it and it's there, as i mentioned i didn't change anything besides my build.gradle.kts in the common folder. Here the main build.gradle.kts of the project:

```python
import java.util.concurrent.TimeUnit

plugins {
    base
    // the ignition module plugin: https://github.com/inductiveautomation/ignition-module-tools
    id("io.ia.sdk.modl") version("0.1.1")
    id("org.barfuin.gradle.taskinfo") version "1.3.0"
}

allprojects {
    version = "1.0.0"
    group = "org.fakester"
}

ignitionModule {
    // name of the .modl file to build
    fileName.set("HadoopDependencies")

    // module xml configuration
    name.set("HadoopDependencies")
    id.set("org.fakester.radcomponent")
    moduleVersion.set("${project.version}")
    moduleDescription.set("A module that adds Hadoop Dependencies.")
    requiredIgnitionVersion.set("8.1.8")
    license.set("license.html")

    // If we depend on other module being loaded/available, then we specify IDs of the module we depend on,
    // and specify the Ignition Scope that applies. "G" for gateway, "D" for designer, "C" for VISION client
    // (this module does not run in the scope of a Vision client, so we don't need a "C" entry here)
    moduleDependencies.put("com.inductiveautomation.perspective", "DG")

    // map of 'Gradle Project Path' to Ignition Scope in which the project is relevant. This is is combined with
    // the dependency declarations within the subproject's build.gradle.kts in order to determine which
    // dependencies need to be bundled with the module and added to the module.xml.
    projectScopes.putAll(
        mapOf(
            ":gateway" to "G",
            ":web" to "G",
            ":designer" to "D",
            ":common" to "GD"
        )
    )

    // 'hook classes' are the things that Ignition loads and runs when your module is installed. This map tells
    // Ignition which classes should be loaded in a given scope.
    hooks.putAll(
        mapOf(
            "org.fakester.gateway.RadGatewayHook" to "G",
            "org.fakester.designer.RadDesignerHook" to "D"
        )
    )
    skipModlSigning.set(true)
}

val deepClean by tasks.registering {
    dependsOn(allprojects.map { "${it.path}:clean" })
    description = "Executes clean tasks and remove node plugin caches."
    doLast {
        delete(file(".gradle"))
    }
}

```

---

<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:** [April 18, 2025, 4:54pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/7 "2025-04-18T16:54:12Z")

</div>

Does the Gateway you're installing this on have the Perspective module installed and running? Can you share the `module.xml` file from your `.modl` file?

---

<div class="post-metadata">

**Author:** ![sebastian.fekete](https://avatars.discourse-cdn.com/v4/letter/s/977dab/32.png) [@sebastian.fekete](https://forum.inductiveautomation.com/u/sebastian.fekete)\
**Post date:** [April 18, 2025, 5:01pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/8 "2025-04-18T17:01:55Z")

</div>

i missed that the perspective module wasn't running. It works now, thanks!

---

<div class="post-metadata">

**Author:** ![sebastian.fekete](https://avatars.discourse-cdn.com/v4/letter/s/977dab/32.png) [@sebastian.fekete](https://forum.inductiveautomation.com/u/sebastian.fekete)\
**Post date:** [April 22, 2025, 1:24pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/9 "2025-04-22T13:24:59Z")

</div>

Hi Kevin, a further question appeard. I installed the modl to the Gateway and tried to access my 3rd party dependencies through the scripting console like this:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/2/224ada6cf5171448840abc4ef10149f29c699031.png)

and it correctly imports, and then executes the following code that i have in Project Library/hadoopTest:

```python
def testFct():
	system.tag.writeBlocking('[default]test',"inFunc")
	try:
		import org.apache.hadoop.conf.Configuration as Configuration
		system.tag.writeBlocking('[default]test',"foundImport")
		conf = Configuration();
		if(conf):
			system.tag.writeBlocking('[default]test',"foundImport+conf")
		else:
			system.tag.writeBlocking('[default]test',"foundImport-conf")
	except Exception as e:
			system.tag.writeBlocking('[default]test',str(e))

```

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/a/caf3cfceb5b8bdc1f0a00e50210c3a87ce3b26e4.png)

But when i create a Gateway Event Tag Change and do exactly the same:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/6/86f413c1b98b5f3913044def8f03096cee5fbbb3.png)

it cannot find the dependencies:  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/9/0/90c147ebb75729841981f1849d2896b70b81301c.png)

I checked it in the module.xml of my build and all dependencies have jar scope="DG"

Any idea what i did wrong?

---

<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:** [April 22, 2025, 1:26pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/10 "2025-04-22T13:26:53Z")

</div>

Modules get loaded in their own isolated ClassLoader, lower in the hierarchy than the platform.

The classes brought in by your module and its dependencies are not available to Gateway-scoped scripting. It only works by chance in Client/Designer scope because there is no ClassLoader hierarchy there - it's entirely flat.

The supported approach is to add scripting functions `system.whatever.foo()` that in turn use the libraries your module includes.

---

<div class="post-metadata">

**Author:** ![sebastian.fekete](https://avatars.discourse-cdn.com/v4/letter/s/977dab/32.png) [@sebastian.fekete](https://forum.inductiveautomation.com/u/sebastian.fekete)\
**Post date:** [April 22, 2025, 8:20pm UTC](https://forum.inductiveautomation.com/t/including-third-party-dependencies-in-ignition-module/105224/11 "2025-04-22T20:20:29Z")

</div>

Thanks for the answer! i will try that and give a short update afterwards!
