Conditional Module Dependency

I’d like to enable some unique functionality in my module depending on whether Vision or Perspective are installed, and this unique functionality needs to access the SDK for the respective module (i.e. if Perspective is installed, my module will need to access classes in com.inductiveautomation.perspective. If Perspective is not installed, it should hide all Perspective related functions).

Is this possible to do with a single module?

With reflective access only, yes. You can use your context to dynamically look up another module, which gives you that module’s gateway hook. From that, you can get its classloader, and ask that classloader to retrieve classes for you. To access more than trivial stuff, you will need “shim” classes in your module that delegate to the real, reflectively retrieved classes. Nowhere in your own classes can you refer to the actual Perspective classes–they won’t be available at runtime.

It is really ugly. You should avoid it.

1 Like

Yeah... that's not something I'd want to support long term.

Sounds like I'll need to either:

  1. Build modules for each combination of Vision/Perspective
  2. Build a base module that provides extension points, and additional extension modules that depend on the base module + a corresponding IA module.

Uhm, I don't think you can do that. The whole point of declaring a dependency is to have your module's classloader created under that module's classloader.

Unless classloaders can have multiple parents?

So, I was under the same impression as Phil, which lead me down a bit of a rabbit hole, but it turns out specifically because of this lack of multi-inheritance, we changed things around on our end. So if you declare, say, Perspective, as a “dependency” of your module, even if Perspective isn’t found your module will still be given a chance to load. The main thing is that Perspective is guaranteed to load before your module, preventing things like trying to register components with Perspective’s component registry before it exists. That’s exactly why we had to change things - the Reporting module adds components to Perspective, but we want Perspective and Reporting to be able to be installed independently.

2 Likes

So, what does the classloader hierarchy look like now?

Wait, does this mean I can make my NoteChart module conditionally supply a component to the Reporting module? (I hadn’t tackled that because I thought the classloader situation would be a nightmare.)

Your module ClassLoader now has a List of parent ClassLoader that are consulted in the order they are present in the list. It’s not really that tricky.

The trickier part was that all the module management code was rewritten to use a proper graph to track and derive dependencies.

1 Like

So, I can declare any module dependencies I want access to? Then I’d just have to make the hook look up the optional classes dynamically, skipping on ClassNotFound …

Hmmm. I LIKE.

Oh! Does all of 8.1 have this?

Yes, looks like it was implemented at least as far back as 8.0.4.

1 Like

Is the module manager smart enough to restart a module if a missing dependency is added? (Or one is removed?)

I’m pretty sure it does, looking at this, but would be wise to verify…

1 Like

Great question Ben! My day is made. :smiley:

2 Likes

If your code that references an optional module dependency is well enough separated you can avoid reflection all together by checking if the other module is installed during startup and then if it’s not, be sure never to invoke that code that references classes that will fail to load.

5 Likes

Ima bookmark this, i feel like i will need this someday xD