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
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?
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.
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.
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?
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).
I added this to my startup code in the gateway which checks for the module dependency, works well, thanks for this.
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?
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 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.
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
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
* 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
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:
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.
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).
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.
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
* 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'.
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?)