# Modules Dependencies

**URL:** <https://forum.inductiveautomation.com/t/modules-dependencies/100514>\
**Category:** Module Development\
**Tags:** ignition81\
**Created:** [January 7, 2025, 2:12am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514 "2025-01-07T02:12:26Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Grace\_Gan](https://avatars.discourse-cdn.com/v4/letter/g/e5b9ba/32.png) [@Grace\_Gan](https://forum.inductiveautomation.com/u/Grace_Gan)\
**Post date:** [January 7, 2025, 2:12am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/1 "2025-01-07T02:12:26Z")

</div>

Is it possible to have a module to depend on another module and error if the module(s) that it depends on is missing?

I can do the following specify the module dependency in `build.gradle.kts`

```python
    moduleDependencies.putAll(mapOf(
         "com.inductiveautomation.perspective" to "GD",
         "module.name" to "GD" )
    )

```

However, the above module dependency only logs a warning in the logs page but it doesn't error the module from installing, is there something i can add to the module startup script to have stricter dependency?

---

<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:** [January 7, 2025, 3:25am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/2 "2025-01-07T03:25:55Z")

</div>

In your module hook's `startup()` method, use the saved gateway context (from `setup()`)to retrieve the other module's hook instance. If null, the other module isn't installed.

---

<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:** [January 7, 2025, 5:53pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/3 "2025-01-07T17:53:26Z")

</div>

Also, for the record, in 8.3 you'll be able to add a `required` attribute to the `depends` declaration in the `module.xml`, in which case the platform will _enforce_ the requirement for you, instead of requiring a soft runtime check. Default behavior without an explicit opt-in to this will be the same as in 8.1.

---

<div class="post-metadata">

**Author:** ![Grace\_Gan](https://avatars.discourse-cdn.com/v4/letter/g/e5b9ba/32.png) [@Grace\_Gan](https://forum.inductiveautomation.com/u/Grace_Gan)\
**Post date:** [January 7, 2025, 9:04pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/4 "2025-01-07T21:04:16Z")

</div>

I've build module A and module B, and I want to have module B depending on module A, will it be possible to reuse resources, e.g. persistent record, from module A in module B?

---

<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:** [January 7, 2025, 9:20pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/5 "2025-01-07T21:20:54Z")

</div>

Yes, module B's classloader with have access to module A's classes. But make sure nothing in your gateway hook references any of that until **after** you do the check I recommended.

That means your B hook class must not reference any of module A's classes, even transitively. That means there will have to be a "boundary" class in B that **does** access A's classes, and is itself loaded indirectly (using a class loader's method to load by string name).

---

<div class="post-metadata">

**Author:** ![Grace\_Gan](https://avatars.discourse-cdn.com/v4/letter/g/e5b9ba/32.png) [@Grace\_Gan](https://forum.inductiveautomation.com/u/Grace_Gan)\
**Post date:** [January 7, 2025, 10:03pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/6 "2025-01-07T22:03:17Z")

</div>

I added this to my startup code in the gateway which checks for the module dependency, works well, thanks for this.

```python
 if (this.context.getModuleManager().getModule("moduleAId") == null) {
            throw new IllegalStateException("Module A is required but not loaded.");
        }  

```

Now I have a resource (persistent record) in moduleA that I want to access from module B that can be found in the following package `module.a.gateway.record.DatabaseRecord`

How do I access this from module B, you mentioned having a boundry class and using class loader's method, could you elaborate more on how to achieve this?

---

<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:** [January 7, 2025, 10:13pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/7 "2025-01-07T22:13:25Z")

</div>

Your boundary class (in B) would reference the module A code like any other (where module A code is marked as a compile dependency).

Make an interface definition that includes method declarations for all of the functionality you need to hit from your module hook, and create a field in your hook of the interface type. Use your hook class's classloader to load the boundary class [by name](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ClassLoader.html#loadClass(java.lang.String)) and place an instance in the interface field.

The rest of your hook methods would call methods, as needed, on that instance, for module life cycle operations.

---

<div class="post-metadata">

**Author:** ![Grace\_Gan](https://avatars.discourse-cdn.com/v4/letter/g/e5b9ba/32.png) [@Grace\_Gan](https://forum.inductiveautomation.com/u/Grace_Gan)\
**Post date:** [January 7, 2025, 10:31pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/8 "2025-01-07T22:31:32Z")

</div>

> Your boundary class (in B) would reference the module A code like any other (where module A code is marked as a compile dependency).

I'm assuming what this means is that the module a need to be included as a compile dependency in module b's build.gradle.kts file so I've done the following

```python
dependencies {
    compileOnly("com.inductiveautomation.ignitionsdk:ignition-common:${rootProject.extra["sdk_version"]}")
    compileOnly("com.inductiveautomation.ignitionsdk:gateway-api:${rootProject.extra["sdk_version"]}")
    // add gateway scoped dependencies here
    compileOnly(project(":module-a")) // added this line
}

```

in my `/moduleB/gateway/build.gradle.kts`

but i get the following error on build

```python

* Where:
Build file 'C:\Users\GraceGan\Documents\projects\ignition-modules\module-b\gateway\build.gradle.kts' line: 15

* What went wrong:
Project with path ':module-a' could not be found in project ':gateway'.

```

My monorepo structure is as follows and the build command is executed from folder /module-b

```python
/ignition-modules
     /module-a
     /module-b

```

---

<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:** [January 7, 2025, 10:35pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/9 "2025-01-07T22:35:12Z")

</div>

I don't use gradle (or maven), so someone else will have help you with that.

---

<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:** [January 7, 2025, 11:01pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/10 "2025-01-07T23:01:41Z")

</div>

> [@Grace\_Gan](#):
>
> `projects\ignition-modules\module-b\gateway\build.gradle.kts'`

This suggests `module-a` and `module-b` are being treated as separate projects.  
To convince Gradle to include one as a dependency of the other, you need to either:

1. Make a top level "super" Gradle project by injecting a `build.gradle.kts` at the `ignition-modules\` directory and having _it_ reference `module-a` and `module-b`'s `build.gradle.kts` files. This effectively gives you a "monorepo" approach.
2. Explicitly "publish" `module-a` for consumption into `module-b`, either to "maven local" (your local computer; complicates CI/CD), a self-hosted Maven repository compatible repository (complicates setup) or a 'real' Maven instance (complicates setup, may cost money for private setup).

---

<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:** [January 7, 2025, 11:10pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/11 "2025-01-07T23:10:21Z")

</div>

> [@paul-griffith](#):
>
> Make a top level "super" Gradle project by injecting a `build.gradle.kts` at the `ignition-modules\` directory and having _it_ reference `module-a` and `module-b`'s `build.gradle.kts` files. This effectively gives you a "monorepo" approach.

[Embr](https://github.com/mussonindustrial/embr) is a good reference for this. I’m using type-safe project accessors, but the same logic applies.

Edit: Forgot I have this floating around too:

> **[GitHub - benmusson/ignition-module-monorepo-example: An example demonstrating multiple Ignition modules...](https://github.com/benmusson/ignition-module-monorepo-example)**
>
> An example demonstrating multiple Ignition modules in the same repository.

---

<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:** [January 7, 2025, 11:13pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/12 "2025-01-07T23:13:42Z")

</div>

Yeah I would 1000% recommend the first approach if you're going to be making continuous updates to either half of this equation. I didn't see your edit earlier that explicitly mentions a monorepo, so it sounds like you're on the right path already.

---

<div class="post-metadata">

**Author:** ![Grace\_Gan](https://avatars.discourse-cdn.com/v4/letter/g/e5b9ba/32.png) [@Grace\_Gan](https://forum.inductiveautomation.com/u/Grace_Gan)\
**Post date:** [January 8, 2025, 12:00am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/14 "2025-01-08T00:00:48Z")

</div>

I added the following

root `build.gradle.kts`

```python
plugins {
    id("io.ia.sdk.modl") version("0.4.0")
}

allprojects {
    group = "cloud.thred.mesh"
    version = "0.0.1-SNAPSHOT"
}

```

and following is my root `settings.gradle`

```python
pluginManagement {
    repositories {
        gradlePluginPortal()
        maven {
            url = "https://nexus.inductiveautomation.com/repository/public" 
        }
    }
}

rootProject.name = "mesh-ignition-modules"

dependencyResolutionManagement {
    repositories {
        // enable resolving dependencies from the inductive automation artifact repository
        maven {
            url "https://nexus.inductiveautomation.com/repository/public"
        }
        mavenCentral()
    }
}

include(
":",
    ":mesh-core",
    ":mesh-oee",
    ":mesh-data-model"
)

```

Seems to be able to detect the subprojects

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

However, adding the following will cause error during the build still

`mesh-ignition-modules/mesh-data-model/gateway/build.gradle.kts`

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

java {
    toolchain {
        languageVersion.set(org.gradle.jvm.toolchain.JavaLanguageVersion.of(11))
    }
}

dependencies {
    compileOnly("com.inductiveautomation.ignitionsdk:ignition-common:${rootProject.extra["sdk_version"]}")
    compileOnly("com.inductiveautomation.ignitionsdk:gateway-api:${rootProject.extra["sdk_version"]}")
    // add gateway scoped dependencies here
    implementation(project(":mesh-core")) // this line is causing the build to fail
}

```

error message as follows

```python
* Where:
Build file 'C:\Users\GraceGan\Documents\projects\mesh-ignition-modules\mesh-data-model\gateway\build.gradle.kts' line: 15

* What went wrong:
Project with path ':mesh-core' could not be found in project ':gateway'.

```

---

<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:** [January 8, 2025, 1:28am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/15 "2025-01-08T01:28:19Z")

</div>

Show the command you’re using to run the build.

---

<div class="post-metadata">

**Author:** ![Grace\_Gan](https://avatars.discourse-cdn.com/v4/letter/g/e5b9ba/32.png) [@Grace\_Gan](https://forum.inductiveautomation.com/u/Grace_Gan)\
**Post date:** [January 8, 2025, 2:08am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/16 "2025-01-08T02:08:30Z")

</div>

`gradle clean build` at the `/mesh-data-model` folder level

---

<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:** [January 8, 2025, 2:44am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/17 "2025-01-08T02:44:31Z")

</div>

You need to execute everything at the top of the monorepo.

From the root, try `gradle :mesh-data-model:clean :mesh-data-model:build`

---

<div class="post-metadata">

**Author:** ![Grace\_Gan](https://avatars.discourse-cdn.com/v4/letter/g/e5b9ba/32.png) [@Grace\_Gan](https://forum.inductiveautomation.com/u/Grace_Gan)\
**Post date:** [January 8, 2025, 2:50am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/18 "2025-01-08T02:50:14Z")

</div>

I think it's working now 🤞

With this in place do I still need to reference the module A code (from module B) via boundary class and class loader or can I reference it directly (by just importing the package?)

---

<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:** [January 8, 2025, 3:01am UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/19 "2025-01-08T03:01:05Z")

</div>

> [@Grace\_Gan](#):
>
> With this in place do I still need to reference the module A code (from module B) via boundary class and class loader or can I reference it directly (by just importing the package?)

If you want to use the implementation from the installed version of Module A, then yes you need to safely-maybe-load at run time.

~~The simplest option is to move the code to a shared library, included it both modules, and make sure your users upgrade both at the same time.~~

Edit: For the use case of sharing PersistenceRecords, you absolutely want to share the same implementation across modules.

---

<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:** [January 8, 2025, 1:04pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/20 "2025-01-08T13:04:21Z")

</div>

> [@bmusson](#):
>
> move the code to a shared library, included it both modules

For `PersistentRecord` cases, this makes me cringe. Don't do it.

---

<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:** [January 8, 2025, 1:17pm UTC](https://forum.inductiveautomation.com/t/modules-dependencies/100514/21 "2025-01-08T13:17:10Z")

</div>

Yeah, great point.

[Next page](https://forum.inductiveautomation.com/t/modules-dependencies/100514.md?page=2)
