Skip to content
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

Extension in dependent jar file for wicket-plugin #4

Open
jchappelle opened this issue Feb 20, 2016 · 3 comments
Open

Extension in dependent jar file for wicket-plugin #4

jchappelle opened this issue Feb 20, 2016 · 3 comments

Comments

@jchappelle
Copy link

I'm trying to migrate our app to use plugins. It's a large app so I'm doing it in small steps. I've managed to refactor out my plugin portion into a separate project and I've created an api that is shared between the main application and the plugin. For my first step I'd like to not have to alter our deployment process by deploying a separate zip file for the plugin. Instead, I'd like to just have a direct dependency on the plugin jar file.

I noticed the following statement on the PF4J project README:

NOTE: Starting with version 0.9 you can define an extension directly in the application jar (you're not obligated to put the extension in a plugin - you can see this extension as a default/system extension). See WhazzupGreeting for a real example.

Is this supported for the wicket plugin? My plugin class never gets instantiated therefore my Extensions never get injected.

If this is supported, then are there any tricks like a custom PluginManager that I have to create? I noticed the DefaultPluginManager looks like it is expecting to read from a File. Should I use ServiceLoader in this scenario? I noticed the WhazzupGreeting uses @extension but the HowdyGreeting uses ServiceLoader.

I'm using the 0.5.0-SNAPSHOT.

Thanks.

@decebals
Copy link
Member

Is this supported for the wicket plugin? My plugin class never gets instantiated therefore my Extensions never get injected.

It's not related to wicket-plugin.

I noticed the WhazzupGreeting uses @extension but the HowdyGreeting uses ServiceLoader.

HowdyGreeting is another story. In PF4J version 0.12.0 (last release) the support for ServiceLoader was improved. PF4J can read META-INF/services (Java Service Provider mechanism) as extensions. Practically if you have already a modular application based on ServiceLoader you can switch instant to PF4J. For more information see https://github.com/decebals/pf4j#serviceloader-interoperability.
HowdyGreeting is a demo class that show the above concept.

I noticed the WhazzupGreeting uses @extension

Yes, WhazzupGreeting is a default/system extension, it's what you lookup. If you take a look at pf4j-demo-app.jar (created in demo application) you can see that this file contains a extensions.idx file (it's identical with the same file from a plugin) with this content:

# Generated by PF4J
ro.fortsoft.pf4j.demo.WhazzupGreeting

Maybe you don't have a extensions.idx file in the classpath of your application. I understand from you that your plugins are for now simple jar files (I think that each jar contains a extensions.idx file) added to the classpath of application.
The code that read extensions from classpath is this and I think that it's OK (for example I see that I read all extensions.idx file from the classpath and this is good if you have multiple jars in classpath that contain extensions - probably your case).

By the way, I am very curios about your journey, about this story (each step, incremental approach) how someone transformed a (relative) complex monotonically web application in a modular one using PF4J 😄

@jchappelle
Copy link
Author

I'll look for the extensions.idx file. I noticed you had documentation about how that gets generated via the annotation processor and some notes about eclipse configuration. I'll try to make sure that's happening.

Since PF4J is compatible with ServiceLoader, my next step is going to be to make my integration points work via the ServiceLoader. I currently have my main menu working that way since I wasn't having any luck with PF4J. There are some other integration points and refactoring to do. Once I'm there and everything is working, I'll put PF4J back in and get it working that way.

This journey is going to take years to fully complete. I'm working on a product that notifies bank customers of suspicious activity and allows them to respond. If they reject, then the software will turn their debit card off immediately. In the beginning we pulled suspicious transactions from another fraud system, now we have written our own fraud detection solution and we are pulling transactions from that as well. There's also another product that validates customer's contact data and takes certain actions if the data is invalid. All of this is in a maven mutli-module project, deployed as a war file. Right now I'm making the fraud product be a separate plugin.

So you can see that our 1 product has become 3 products and I've seen what happens from here. 10 years ago we started another banking product as 1 and it grew into 13 deployed in a single war file. That product has hundreds of banks running it and it is a SUPER monolith. The verification/fraud product I'm on now is only 4 years old so I'm starting with it to learn the best way to break this up. The real beast is still out there, waiting to be slayed :-).

I've thought of writing a blog about breaking a Wicket/Spring/Hibernate app up slowly with PF4J. A lot of the things I'm learning aren't even PF4J specific they are just architectural things. Due to the lack of articles out there like that I think it would be useful to someone.

@jchappelle
Copy link
Author

I've created an ExtensionProvider interface like this:

public interface ExtensionProvider
{
    <T> List<T> getExtensions(Class<T> type);
}

All of our code depends on this. The only implementation I have is a ServiceLoaderExtensionProvider. Tomorrow I'll create a PF4JExtensionProvider that uses the PluginManager.getExtensions and hopefully it just works.

The biggest challenge was separating our code and creating abstractions for integration points. I say integration points instead of extension points because there are places where our plugin needs access to the "core" app, but I didn't want to expose a panel or page in our api project because that would grow into a lot of things being in the api. So I created a ComponentIntegrationService in our api that has method calls to get access to only those components that are needed. Instead of returning the actual component, I return the parent most class that makes sense, like WebMarkupContainer or AbstractLink. This way the "core" app can change the implementation without breaking the plugins.

Right now there are only runtime dependencies on the fraud app components. Which allows me to make sure no other developers reference classes they shouldn't from the "core" app but also frees me from having to alter our deployment process for now.

One thing I haven't figured out is how integration tests will work. Also, once the runtime dependency is removed, I'm not sure how I will launch the app from within eclipse and have the plugins get installed when I launch. I see how your run-demo.bat works but our projects are not all under the same hierarchy. I think I should be able to do something really similar though.

Very interesting stuff and fun to program!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants