New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support optional plugin dependencies #266

Closed
pinhead84 opened this Issue Dec 27, 2018 · 18 comments

Comments

Projects
None yet
2 participants
@pinhead84
Copy link
Contributor

pinhead84 commented Dec 27, 2018

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.

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Dec 28, 2018

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.

@pinhead84

This comment has been minimized.

Copy link
Contributor Author

pinhead84 commented Dec 28, 2018

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.

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Dec 28, 2018

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

@pinhead84

This comment has been minimized.

Copy link
Contributor Author

pinhead84 commented Jan 3, 2019

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

@pinhead84

This comment has been minimized.

Copy link
Contributor Author

pinhead84 commented Jan 5, 2019

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

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Jan 5, 2019

@pinhead84
Thanks for your excellent research work!

@decebals decebals added the enhancement label Jan 7, 2019

@pinhead84

This comment has been minimized.

Copy link
Contributor Author

pinhead84 commented Jan 11, 2019

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?

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Jan 11, 2019

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!

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Jan 11, 2019

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

@pinhead84

This comment has been minimized.

Copy link
Contributor Author

pinhead84 commented Jan 13, 2019

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?

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Jan 14, 2019

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.

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Jan 14, 2019

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.

@pinhead84

This comment has been minimized.

Copy link
Contributor Author

pinhead84 commented Jan 30, 2019

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.

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Jan 31, 2019

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

@pinhead84

This comment has been minimized.

Copy link
Contributor Author

pinhead84 commented Feb 1, 2019

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

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Feb 1, 2019

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.

@decebals

This comment has been minimized.

Copy link
Member

decebals commented Feb 2, 2019

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

@pinhead84

This comment has been minimized.

Copy link
Contributor Author

pinhead84 commented Feb 2, 2019

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.

@pinhead84 pinhead84 closed this Feb 2, 2019

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