Skip to content

support optional plugin dependencies #266

Closed

Description

@pinhead84

Under certain situations it might be very helpful to support optional dependencies between plugins. For example the following scenario is currently pretty difficult to realize with pf4j:

pf4j-optional-extensions

One plugin adds contact management and the other plugin adds calendar functionality to an application. Both plugins provide a form to modify contacts / calendar entries, that can be extended by other plugins through the ContactsFormExtensionPoint / CalendarFormExtensionPoint.

Let's assume, that the contacts plugin adds a panel to the calendar form, that shows assigned contacts for a certain calendar entry. On the other hand the calendar plugin adds a panel to the contacts form, that shows assigned calendar entries for a certain contact.

In this case, both plugins would depend on each other. I would have to define these dependencies in their META-INF/MANIFEST.MF accordingly.

But what happens, if one of the plugins is missing, because the application user might not have installed it? - Currently pf4j wouldn't load the contacts plugin, if the calendar plugin is missing and vice versa (due to the defined dependencies in META-INF/MANIFEST.MF). But in this case, the contacts plugin should still be usable - even if the calendar plugin is not present.


With the current version of pf4j I only see one solution for this problem. I would have to remove the dependencies between the plugins and have to create a separate plugin, that provides the com.example.plugins.contacts.addons package. This additional plugin would only be loaded, if contacts and calendar plugin are present.

This might be an acceptable solution for small application. But if we think a little bit bigger, this approach might lead to a huge number of plugins only to reflect those inner plugin dependencies.

  • Four application plugins would require six additional plugins to reflect all possible inner plugin dependencies.
  • Six application plugins would require fifteen additional plugins to reflect all possible inner plugin dependencies.
  • Or in general: A number of n application plugins would require n * (n-1) / 2 additional plugins to reflect all possible inner plugin dependencies.

I hope my math is right. ;) - But even if the formula is wrong, it should show, that this possible workaround can lead to a big mess on more complex applications with a bigger number of plugins.


But how did others solve this problem? - For example JPF solved this problem by allowing optional plugin dependencies. I think, this would also be the most elegant solution for pf4j.

According to the example above: The contacts plugin could define an optional dependency to the calendar plugin (and vice versa). If the calendar plugin is not present, the contacts plugin is still available for the application - only the CalendarContactsFormExtension is not loaded (because it requires the calendar plugin to be available).


What do you think about all this? - Do you also see this as a problem or am I missing something?

From my point of view the suggested feature would be a big improvement for pf4j, that could also be helpful for others. I'm currently implementing these optional dependencies. There is still some testing and fixing to do, therefore I can't provide source code yet. But are you generally interested in a pull request for this feature?

If not, I would have to fork the project and maintain the modified source code for myself because my application heavily relies on this. I'm hoping to get a positive response, because I would rather support your project instead of forking and maintaining it for myself.

Activity

  1. decebals commented on Dec 28, 2018

    @decebals
    Member

    The feature seems to be valuable, so we can add it.
    As hints to implementation this geature:

    • add a new method isOptional in PluginDescriptor.
      We can use sign ? to the end of string to specify that a dependency is optional, like
    com.acme.contact@2.1.0?
    

    Maybe is necessary extra work, we can discuss.

  2. pinhead84 commented on Dec 28, 2018

    @pinhead84
    ContributorAuthor

    Thanks for your quick and positive response.

    add a new method isOptional in PluginDescriptor.
    We can use sign ? to the end of string to specify that a dependency is optional, like

    com.acme.contact@2.1.0?
    

    I was thinking the same and already did that in my local / testing implementation. But currently I put the question mark behind the plugin name instead of the version number, like

    com.acme.contact?
    com.acme.contact?@2.1.0
    

    But if you prefer

    com.acme.contact@2.1.0?
    

    over

    com.acme.contact?@2.1.0
    

    I will change my implementation accordingly.

    adjust DependencyResolver

    I also did that in my local / testing implementation.


    The modifications are basically working quite well within my application. But there are still some class loading issues to solve.

    According to my example above, there is currently a NoClassDefFoundError thrown for the ContactsCalendarFormExtension, if the calendar plugin is not available. I was thinking about this problem for the last few days. I believe, that I found a good solution for it. But it needs some more testing on my side before I can provide a pull request. You will hear from me about it in the next few days.

  3. decebals commented on Dec 28, 2018

    @decebals
    Member

    I was thinking the same and already did that in my local / testing implementation. But currently I put the question mark behind the plugin name instead of the version number, like ...

    I think that is a detail where to put ? sign. Maybe as first character is better 😄.

  4. pinhead84 commented on Jan 3, 2019

    @pinhead84
    ContributorAuthor

    As basis for discussion I've published pull request #270 with my current implementation of optional plugin dependencies.

  5. pinhead84 commented on Jan 5, 2019

    @pinhead84
    ContributorAuthor

    @decebals
    In one of our current discussions you've asked, how other plugin frameworks have solved this issue. I've already referred to JPF, which uses a similar solution to my pull request #270.

    But out of curiosity I took a look into the documentation of IntelliJ, Netbeans & OSGi about how they are approaching this issue:

    IntelliJ

    According to this XML configuration plugins can have optional dependencies on each other:

    <idea-plugin url="http://www.jetbrains.com/idea">
      <name>VssIntegration</name>
      <id>VssIntegration</id>
      <description>Vss integration plugin</description>
      <change-notes>Initial release of the plugin.</change-notes>
      <version>1.0</version>
      <vendor url="http://www.jetbrains.com" email="support@jetbrains.com" />
    
      <!-- The unique identifiers of the plugins on which this plugin depends. -->
      <depends>MyFirstPlugin</depends>
    
      <!-- Optional dependency on another plugin.
           If the plugin with the "MySecondPlugin" ID is installed,
           the contents of mysecondplugin.xml (the format of this file
           conforms to the format of plugin.xml) will be loaded. -->
      <depends optional="true" config-file="mysecondplugin.xml">MySecondPlugin</depends>
    
      <!-- ... some more stuff ... -->
    </idea-plugin>

    Also an extension may explicitly define, on which other plugin it depends:

    <idea-plugin url="http://www.jetbrains.com/idea">
      <!-- ... some more stuff ... -->
    
      <!-- Declare extensions to access extension points in the IntelliJ Platform. -->
      <extensions defaultExtensionNs="com.intellij">
        <appStarter implementation="MyTestPackage.MyTestExtension1" />
        <applicationConfigurable implementation="MyTestPackage.MyTestExtension2" />
      </extensions>
    
      <!-- Declare extensions to access extension points in a custom plugin -->
      <extensions defaultExtensionNs="MyPluginID">
         <MyExtensionPoint1 key="keyValue" implementationClass="MyTestPackage.MyClassImpl"></MyExtensionPoint1>
      </extensions>
    
      <!-- ... some more stuff ... -->
    </idea-plugin>

    It looks to me, that IntelliJ goes similar ways like JPF or as it is proposed in my pull request.

    Netbeans

    It seems, that the Netbeans module framework does not support optional plugin dependencies. According to their documentation, they are solving this issue by separate plugins to reflect inner plugin dependencies:

    Eager modules are often used for "bridges" between otherwise independent pieces of functionality, represented as regular modules. If both of those modules are enabled, the eager bridge will be as well, integrating them using some optional service. They may also be used as add-ons to regular modules distributed via other channels; or as platform-specific modules that will be enabled only according to operating system tokens.

    This approach leads to the problems, I've pointed out earlier:

    With the current version of pf4j I only see one solution for this problem. I would have to remove the dependencies between the plugins and have to create a separate plugin, that provides the com.example.plugins.contacts.addons package. This additional plugin would only be loaded, if contacts and calendar plugin are present.

    This might be an acceptable solution for small application. But if we think a little bit bigger, this approach might lead to a huge number of plugins only to reflect those inner plugin dependencies.

    A number of n application plugins would require n * (n-1) / 2 additional plugins to reflect all possible inner plugin dependencies.

    Therefore I don't really like the Netbeans approach to this problem. But maybe I've missed something...

    OSGi / Eclipse

    As far as I know, the Eclipse plugin framework is more or less based on OSGi. Therefore I'm looking at OSGi instead.

    According to this tutorial optional plugin dependencies are support by OSGi:

    Sometimes a given package imported by a bundle isn’t strictly necessary for it to function properly. Consider an imported package for a nonfunctional purpose, like logging. The code in a given bundle may have been programmed to function properly regardless of whether a logging facility is available. To express this, OSGi provides the resolution directive for the Import-Package manifest header to mark imported packages as optional.

    Bundle-ManifestVersion: 2
    Bundle-SymbolicName: org.foo.paint
    Bundle-Version: 2.0.0
    Bundle-Name: Paint
    Import-Package: javax.swing; resolution:="optional", org.foo.shape; org.foo.shape.impl; version="2.0.0"; resolution:="optional"
    

    Something similar is documented for Eclipse plugins and other OSGi based solutions.

    OSGi is quite complex and I did not fully understand all of their concepts. But it looks to me, that it also provides solutions to check plugin requirements of certain extension - e.g. with a custom BundleActivator or by overriding Bundle#loadClass(java.lang.String).

    As far as I understand, OSGi also wouldn't force the developer to create separate plugins to reflect inner plugin dependencies (like Netbeans or PF4J currently does).


    My personal conclusion about this: Two of three comparable solutions to PF4J are addressing optional plugin dependencies. I guess those requirements are not as rare, as one might think. ;)

  6. decebals commented on Jan 5, 2019

    @decebals
    Member

    @pinhead84
    Thanks for your excellent research work!

  7. pinhead84 commented on Jan 11, 2019

    @pinhead84
    ContributorAuthor

    Thank you for merging the pull request. I believe, this is a quite a big improvement and makes PF4J even greater.

    If you like, I would provide a documentation for #265 and #270. I guess, that I can create a pull request in your pf4j.github.io repository for that?

  8. decebals commented on Jan 11, 2019

    @decebals
    Member

    If you like, I would provide a documentation for #265 and #270. I guess, that I can create a pull request in your pf4j.github.io repository for that?

    Even please. Optional plugins is your nice contribution, so you must close the circle :) Thanks!

  9. decebals commented on Jan 11, 2019

    @decebals
    Member

    I uploaded a new SNAPSHOT version that is up to date.

  10. pinhead84 commented on Jan 13, 2019

    @pinhead84
    ContributorAuthor

    I'll send you some pull requests for the pf4j.github.io repository in the next one or two weeks. Currently I'm quite busy with the migration of my application to Java 11. Therefore documentation may take some time.

    I uploaded a new SNAPSHOT version that is up to date.

    That's good to know. Do you have any plans about a new release in the next few weeks? - Or do you prefer to wait until the documentation was updated?

  11. decebals commented on Jan 14, 2019

    @decebals
    Member

    That's good to know. Do you have any plans about a new release in the next few weeks? - Or do you prefer to wait until the documentation was updated?

    It would be nice to have a minimum documentation (maybe copy/paste from PR) until you have time to add a more complete documentation.

  12. decebals commented on Jan 14, 2019

    @decebals
    Member

    I updated SNAPSHOT version again. Please use this version in your project until release a new version. The idea is to test as much as possible, in a real environment, the new version.

  13. pinhead84 commented on Jan 30, 2019

    @pinhead84
    ContributorAuthor

    You should find a pull request with the some additions to the manual.

    The idea is to test as much as possible, in a real environment, the new version.

    I've still not released my application with the modifications on PF4J to the public. I guess it will take 1-2 weeks until the changes can be tested on a wider user base. But up to know I did not find any problems during my local tests.

  14. decebals commented on Jan 31, 2019

    @decebals
    Member

    @pinhead84
    Now that documentation was improved (thanks for your contribution), and the new concepts are documented, I think that we can release a new version. If something is wrong (you isolated well enough the new features, to not impact the old features if the new features are not used) we can release instantly a new bug fix version.

  15. pinhead84 commented on Feb 1, 2019

    @pinhead84
    ContributorAuthor

    Thank you for being open about my proposals. I appreciate the effort you've put into PF4J and that my pull requests are critically questioned. From my point of view PF4J is well designed, easy to learn and deserves more attention. It's a great and straightforward alternative to bloated solutions like OSGI. Keep up the great work!

    With the last changes PF4J is more or less feature complete for my personal use case. Therefore I guess you won't get any bigger proposals by me in the future. But of course I'll continue to look out for bugs and possible bugfixes. ;)

  16. decebals commented on Feb 1, 2019

    @decebals
    Member

    Thank you for being open about my proposals. I appreciate the effort you've put into PF4J and that my pull requests are critically questioned. From my point of view PF4J is well designed, easy to learn and deserves more attention. It's a great and straightforward alternative to bloated solutions like OSGI. Keep up the great work!

    Thank you for your contribution and for your patience!

    Therefore I guess you won't get any bigger proposals by me in the future. But of course I'll continue to look out for bugs and possible bugfixes. ;)

    PF4J is a community driven project so if anybody has a great idea (easy or difficult to implement) I will accept that idea with pleasure.

  17. decebals commented on Feb 2, 2019

    @decebals
    Member

    New version (2.6.0) released.
    Can we close this issue?

  18. pinhead84 commented on Feb 2, 2019

    @pinhead84
    ContributorAuthor

    I'm happy to see the new version being released.

    Can we close this issue?

    Yes. I'll close this issue if you don't mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions