Closed HomeOfTheWizard closed 1 year ago
What bytecode is your output? Maven 3.9.1 uses Sisu 0.3.5 that supports only up to Java 11 bytecode, anything above that will be ignored by Sisu.
If all good (re bytecode) you still should not mix Mojo annotations/API and JSR330. Fix for your issue should be:
@Inject
to the field private MyComponent component;
Thanks for the tips and the quick reply ! 👍 My issue actually was in the component that I tried to inject. I was trying to bind a spring managed bean as instance of MyComponent.class. The example above with a basic instance created with the constructor works actually. I will try to figure out how to bind a spring managed instance.
And one more hint: ift you do not depend on Guice, just by using this single Module class, then instead of module just create:
@Singleton
@Named
public class MyComponentProvider implements javax.inject.Provider<MyCoomponent> {
public MyComponent get() {
...construct my component
}
}
Thanks again, this can be very useful! I am actually trying to bind multiple beans from a library. I will give a try to the spring-guice project, this may help me bind all beans in a spring container in one go. If it does not work I will create a provider for all as you suggested
@cstamas one last question before I switch the subject, custom modules annotated with @Named
are injected in Guice with the default no args constructor right ?
I am asking the question cause by looking quickly to the documentation of the spring-guice project, I will have to use the constructor to create my custom module that will bridge my Spring beans with Guice.
Yes, they must have default ctor
I have the same issue with the bean injected by the SpringModule class of spring-guice project.
Here is how I autocreate the module:
@Named
public class MySpringModule extends SpringModule {
public MySpringModule(){
super(new AnnotationConfigApplicationContext(MySpringConfig.class));
}
}
The beans does not get injected as Guice component in my Mojo.
Even when I try to inject a spring managed bean myself in a custome Module like below, it does not work.
...
public void configure() {
applicationContext = new AnnotationConfigApplicationContext(MySpringConfig.class);
var mySpringBean = applicationContext.getBean(MySpringBean.class);
bind(MySpringBean.class).toInstance(mySpringBean);
bind(MyComponent.class).toInstance(new MyComponent());
}
...
The normal component with ctor is injected, but not the spring managed instance.
However if I try to instantiate the spring container in a Guice component itself, get the bean and use it, it works fine.
@Named
@Singleton
public class MyComponent {
private ApplicationContext applicationContext;
private void displayAllBeans() {
String[] allBeanNames = applicationContext.getBeanDefinitionNames();
for(String beanName : allBeanNames) {
System.out.println(beanName);
}
}
public void sayHelloFromSpringBean(){
var mySpringBean = applicationContext.getBean(MySpringBean.class);
mySpringBean.sayHello();
}
public void hello() throws ClassNotFoundException {
System.out.println( "Hello! I am a component that is being injected with sisu" );
applicationContext = new AnnotationConfigApplicationContext(MySpringConfig.class);
System.out.println( "Spring container initiated" );
displayAllBeans();
System.out.println( "Beans listed" );
sayHelloFromSpringBean();
}
}
Is there a difference between Sisu and Guice that can cause this issue ? I applogies but I am new to guice, do you have any tips that can help me dig and see why it is not injected ?
@cstamas I have downgraded to JDK 11, removed the ctor and used inject in the field itself. But nothing changed 😢 My basic java instance is injected but not the spring managed instance.
Am not a spring guy, but best would be if you create a reproducer in some shared repo, so i can look. Nothing guaranteed
Are you doing this in a plugin or extension? IIRC Maven doesn't expose the Guice API to plugins, so you'll need to stick with the javax.inject API using approaches like a writing a @Named Provider
to delegate to Spring.
@cstamas I fail to use a guice module. I managed to inject a simple POJO in sisu but not an instance binded by my custom module.
I would like to debug to see what happens but I couldn't do it with mvnDebug
It doesn't stop on the break points I put in the modules.
It looks like it listens for a debugger only once all the modules are created and the code of the mojo is ready to be executed.
I also tried to do some logging, but it also fails to print any output during modules initialisation.
Any System.out.println
is printed.
I also tried to use the plexus logger org.codehaus.plexus.logging.Logger
, but got the same result as with system.out.println
Is there a way to debug the initialisation of the modules in sisu ? Sorry for such basic questions, I am new to maven development.
Just for your information, To be sure that there is nothing wrong with my code, library and configs, I tested the same in an application explained as in this documentation https://eclipse.github.io/sisu.inject/#ready
It worked fine. I managed to bind the bean coming from the spring library. Here is the repo of the code that works. https://github.com/HomeOfTheWizard/sisu-spring-test
But I just cant make it work when I package them all in a plugin and run inside maven. Here is the repo of the plugin that cannot bind the same bean as the app above. https://github.com/HomeOfTheWizard/spring-bridge-demo-maven-plugin
Hi - I suspect it's because Maven doesn't expose the Guice API (ie. com.google.inject) from maven-core to plugins.
This means your plugin loads com.google.inject classes from the plugin's class-loader rather than using the classes from maven-core, so you end up with different Module classes - one loaded by Maven itself and the other loaded by your plugin. Sisu sees the same com.google.inject classes as maven-core, which is why it won't automatically pick up your module (because it uses the different com.google.inject classes loaded by the plugin.)
This also explains why your example works outside of Maven, but not when it's wrapped in a plugin.
You should still be able to re-use the SpringModule, but you'll need to expose the bindings a different way - I'll try to find time over the weekend to write up an example.
Message ID: @.***>
@mcculls I tried to see if anything changes with an extention instead of a plugin, but it failed with same result. I am starting to loose all hope here :'( It would be greate if you can help me see how to debug the modules loaded by plugin/extension.
@mcculls ok! sorry! I just understood what you mean :) you were talking about why I cannot debug the modules. And actually you were right from the beggining! my modules are not picked up at all !
My POJOs that I inject in my MOJO are actually not binded by my custome modules! Apparently the sisu can inject simple POJOs with default ctors, without any annotation on the componenet, and without an explicit binding ! interesting.
Now I am wondering what am I doing wrong, because my custome modules dont' work at all, doesnt matter if I create them in a plugin or extension. I am doing exactly what is shown here though. https://github.com/eclipse/sisu.plexus/wiki/Plexus-to-JSR330#custom-bindings
Which is weird is that, I see MySpringModule as a component in the plexus container when I lookup with a LifeCycleParticipant
as shown here
https://maven.apache.org/studies/extension-demo/xref/
I simply do
session.getContainer().lookup(AbstractModule.class,"mySpringModule")
And it seems to have been picked up by sisu. But any System.out.println I put inside, do not show anything on console...
No worries - I wondered if it might be using the default constructors.
I'll need to confirm, but I think the issue is that Maven doesn't currently expose the Guice module API - so while you can use modules in your plugin, and query them via the lookup API, classes like AbstractModule.class
are actually different to classes with the same name inside Maven core. (The class-loader mechanism in Maven means that you can load classes with the same name in the core and different plugins and they are actually different classes - only those explicitly exposed from the core will be the same across the core and all plugins.)
This means that Sisu, which lives inside core, cannot cast your module instance to its AbstractModule
class - because the AbstractModule
class loaded by your plugin is not the same as the AbstractModule
class loaded by Maven core.
One approach would be to create a Guice injector inside your plugin and have JSR330 annotated Provider
s delegate to that shared injector instance.
No worries - I wondered if it might be using the default constructors.
I'll need to confirm, but I think the issue is that Maven doesn't currently expose the Guice module API - so while you can use modules in your plugin, and query them via the lookup API, classes like
AbstractModule.class
are actually different to classes with the same name inside Maven core. (The class-loader mechanism in Maven means that you can load classes with the same name in the core and different plugins and they are actually different classes - only those explicitly exposed from the core will be the same across the core and all plugins.)This means that Sisu, which lives inside core, cannot cast your module instance to its
AbstractModule
class - because theAbstractModule
class loaded by your plugin is not the same as theAbstractModule
class loaded by Maven core.One approach would be to create a Guice injector inside your plugin and have JSR330 annotated
Provider
s delegate to that shared injector instance.
@mcculls Ok! Thanks! it is much clearer now :)
So the only way to inject objects into my plugin is to use a @Named
Component or a javax.inject.Provider
?
Also just to be sure to understand: What is shown in this example https://github.com/eclipse/sisu.plexus/wiki/Plexus-to-JSR330#custom-bindings is to show a way to add custom bindings to maven core, but not to the plugin's injection mechanism ?
Right now I believe that's the case for Maven, because IIRC com.google.inject is not exported from core. Other apps that migrated from Plexus (like Nexus) did expose it, and that's where that advice came from.
@mcculls Thanks a lot for the clarification. Is there any work going on to export this api in future versions of maven ?
Also, I think this tutorial is a bit misleading. https://github.com/eclipse/sisu.plexus/wiki/Plexus-to-JSR330#custom-bindings Because there are lots of SO threads opened by people who fell for the same. If you can confirm that custom bindings don't work in plugins, this ticket would be a great helper for all.
On Mon, May 29, 2023, 4:43 PM Stuart McCulloch @.***> wrote:
Right now I believe that's the case for Maven, because IIRC com.google.inject is not exported from core. Other apps that migrated from Plexus (like Nexus) did expose it, and that's where that advice came from.
— Reply to this email directly, view it on GitHub https://github.com/eclipse/sisu.plexus/issues/35#issuecomment-1567227543, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJIV32OWZKWX2Q5TVZVPEVTXISYYLANCNFSM6AAAAAAXZ7HWJY . You are receiving this because you modified the open/close state.Message ID: @.***>
@mcculls I tried to use modules in an extension but couldt make it work either. https://github.com/HomeOfTheWizard/demo-maven-extension. When I lookup in the container, I see the module is picked up, but cannot find the component that I bind with the module. Maybe my way of looking for the binded object is wrong ? Here is what I do
session.getContainer().lookup(AbstractModule.class,"myModule"); //gets MyModule.class
session.getContainer().hasComponent("myComponent"); // return false.
BTW the session.getContainer()
is deprecated. Maybe there is a better API to search in the sisu container ?
I will create another issue for the extention and keep this issue for waiting your confirmation on the usage of custom bindings in plugins
I too would like to be able to use custom bindings in plugins. The documentation is either misleading or plain wrong. I have managed to debug a plugin execution, and I can't see any code anywhere that ever would register a custom Module
even if the classpath was sorted out. The closest I could see was the QualifiedTypeBinder
which is used by PlexusTypeBinder
but is never called by the plugin system during my custom plugin execution.
UPDATE: after some more debugging I can see that the PlexusTypeBinder
is not called because (as surmised above) the custom Module
class cannot be loaded. If I explicitly add Guice to the classpath of my plugin then the binder is called but then doesn't bind anything because (as surmised above) the custom Module
is not of type Module
(different class loaders). This seems like a bug to me. Sisu obviously expects it to work and Maven and/or sisu.plexus has switched it off.
@dsyer thanks for the analysis, I wanted to debug as well but didnt have time. That confirms what stuart was saying previously: It must have been switched off on purpose. That leaves us with a single choice, doing the bridge with an with a maven extention ? @mcculls I can try to work out a PR if you are willing to accept to open custom modules for plugins
I think opening custom modules for plugins using the available sisu.inject mechanisms would be very dangerous. There's a reason the class loader boundaries are so strict - it's what keeps Maven plugins vaguely sane and working in spite of big changes in the Maven project infrastructure.
So I think we have to ask ourselves, what is it we actually need here? I think for me it is 2 things: 1) make my Mojo
more testable by leaving it open to manual wiring, 2) re-use existing custom bindings from libraries.
The best I could come up with was to include Guice on the plugin classpath and create an Injector
manually. You don't get to contribute custom bindings to the surrounding Plexus container, but you can inject stuff from it, e.g.
@Named
class InjectorProvider implements Provider<Injector> {
private final Injector injector;
@Inject
InjectorProvider(MavenSession session) {
injector = Guice.createInjector(new MyModule());
// ... do stuff with MavenSession etc.
}
public Injector get() {
return injector;
}
}
Then inject that Injector
either directly into your Mojo
or (better but more verbose) create a Provider
for each of the components you want, e.g.
@Named
class ServiceProvider implements Provider<Service> {
private final Injector injector;
@Inject
ServiceProvider(Injector injector) {
this.injector = injector;
}
public Service get() {
return injector.getInstance(Service.class);
}
}
This makes it explicit that you don't expect all the bindings in your library to end up in the plugin Plexus container, which I think is probably a good thing. It's a bit unfortunate that you have to write boilerplate code to expose the components that you need in your Mojo
, so I would be prepared to live with a solution that made them all available. What would make this a lot nicer would be if we could get rid of the boilerplate (without opening the class loader boundaries). I don't know if that's do-able - probably it would involve a small helper library that you need on your plugin classpath. I might play around with that idea a bit.
I agree with that. I will try to make a library for the boilerplate code for using modules in plugins.
What about an extention ? is it acceptable to delegate the responsability of not breaking everything in plexus to a developper of a maven extention and allow him/she to add custom bindings? I am refering to that thicket https://github.com/HomeOfTheWizard/demo-maven-extension
if so we can as well allow plugins use existing bindings from external libraries.
I don’t know if an extension helps. The class loader boundary is constructed differently I think, but the isolation is just as strict. Happy to be wrong.
Anyway, I have a proof of concept for a library that creates a new injector like in the code above. I’ll play around with it some more and see if it’s worth publishing.
if you accept help I would love to contribute ^_^
I put the code here: https://github.com/scratches/plugin-demo/.
Ok I see that you used a plugin as a dependency of the first plugin to generate automatically the providers, I was trying to do the same during launch break, but you were faster :D I think you are almost done so I will let you continue. Anyway thanks a lot. I dont know what to do about this ticket however. should I just close it ?
I think this issue should be fixed as a documentation issue at the very least, so I would leave it open. Maybe change the summary?
Since @cstamas had initially proposed to use the Provider
interface, I had started to play with it as well.
I managed to create a configurable plugin that is generic enough to serve a more general purpose.
https://github.com/HomeOfTheWizard/spring-bridge-maven-plugin
My goal was to have the spring-cloud vault library in my Vault Maven Plugin.
It can be configured to use any spring context configuration from any spring library, and doesn't require building the plugin.
However I still prefer using an extention and use the Spring-Guice project, So I tried to create a configurable extension.
https://github.com/HomeOfTheWizard/spring-bridge-maven-extension.
It has some bugs but nothing that cannot we worked around in my opinion.
@dsyer if you are interested to release a plugin and accept a modest help from a newbe, I can create a PR on your scratch project to make it configurable.
Also, @mcculls , I would like to contribute to fix the documentation.
However the doc is a github wiki so I cannot do a PR.
Would you be ok if we create github page documentation on this repo and move the wiki page to the github page ?
If yes I can create a PR for that as well.
@HomeOfTheWizard thanks for all your work on this - yes, moving the doc to a github page is fine with me
Hello,
Not sure what I am doing wrong but I cannot load a custome Guice/Sisu module to inject cutom bindings as explained in the documentation
https://github.com/eclipse/sisu.plexus/wiki/Plexus-to-JSR330#custom-bindings
I am using maven 3.9.1 and developping with OpenJDK 17.
Here is my pom.xml
Here is my Mojo:
Here is my Module with custom bindings:
Can you please help me sort this out ?