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).
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.
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.
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.