conflicts with existing, non-compatible bean definition of same name and class - spring

i get this error after i obfuscate my project , eventghout only my implementation class com.company.project.f.a.a.b is qualified by #Component("myImpl")
java.lang.IllegalStateException: Annotation-specified bean name
'myImpl' for bean class
[com.company.project.f.a.a.b] conflicts with existing, non-compatible
bean definition of same name and class [com.company.project.f.a.a.a]
i don't realy understand why spring attributes the same name for both the interface and the implementation . could it be something wrong with my Proguard config ?

What you can try is to keep #Component classes as is by telling proguard to do so through a -keep option.
In case if you use a proguarg maven plugin you can set this option as:
<plugin>
... ...
<artifactId>proguard-maven-plugin</artifactId>
... ...
<configuration>
<obfuscate>true</obfuscate>
<options>
... ...
<option>-keep #org.springframework.stereotype.Component class *</option>
... ...
</options>
... ...
</configuration>
</plugin>
It'll keep #Component components reverse engineer and Spring friendly :)

i found the source of the problem , Proguard merges interaces with their implementation if the optimizer is enabled.
i added
-dontoptimize
and the probleme is solved

for #Component
<option>-keep #org.springframework.stereotype.Component class *</option>
for #Controller
<option>-keep #org.springframework.stereotype.Controller class *</option>
for #Service
<option>-keep #org.springframework.stereotype.Service class *</option>

Sometimes the problem occurs if you have moved your classes around and it refers to old classes, even if they don't exist.
In this case, just do this :
mvn eclipse:clean
mvn eclipse:eclipse
This worked well for me.

Related

Spring - show git commit ID on custom health endpoint

I am trying to show the Git version info (branch, commit etc) on my custom health endpoint.
I tried using management.info.git.mode=full + git-commit-id-plugin but there is no direct way to extract the git info into a Java class. If there is, this will be the ideal way.
I also tried the same git-commit-id-plugin with Value annotations in my Java class like so #Value("${git.commit.id}") but Spring can't find the property values. I see the git.properties file created in the target dir.
What am I missing here? thanks in advance
We have to configure PropertyPlaceHolderConfigurer bean so that we can able to access the property file generated by the plugin, Please use the below code for your reference,
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
PropertySourcesPlaceholderConfigurer propsConfig
= new PropertySourcesPlaceholderConfigurer();
propsConfig.setLocation(new ClassPathResource("git.properties"));
propsConfig.setIgnoreResourceNotFound(true);
propsConfig.setIgnoreUnresolvablePlaceholders(true);
return propsConfig;
}
then in your custom health check class, you can use,
#Value("${git.commit.id}")
private String commitId;
I hope this will resolve your problem.
With Spring Boot 2 you can get this information using git-commit-id-plugin in info endpoint. This is how you can configure it
POM File
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
</plugins>
</build>
Sample Response http://localhost:8080/actuator/info
{
"git":{
"branch":"some-name",
"commit":{
"id":"ef569c2",
"time":1579000598.000000000
}
},
"build":{
"artifact":"xxx",
"name":"xxxx",
"time":1579020527.139000000,
"version":"0.0.1-SNAPSHOT",
"group":"xxxx"
}
}
The easies way is to use the commit plugin:
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.build.generate-git-info
It generates git.properties. Then Spring autoconfiguration jumps in. When git.properties is available in the classpath, it creates GitProperties bean:
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.info.git-commit-information
Simply inject GitProperties to your bean and use it.

Creating A Bean From Another Maven Module

Creating a bean in a Maven project is rather simple. You add the #Autowiredannotation to the bean you wish to create and then declare its config in the spring-config.xml:
#Autowired
private ExampleBean exampleBean;
.
.
.
<bean id="exampleBean" class="path.to.your.bean">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
But what if I have a multi-moduled Maven project and the bean I would like to create is from another module referenced in the build path? How do you go about creating that bean?
I thought about the annotation #Resource but it didn't seem to "detect" the bean that is coming from another module added in the build path.
UPDATE 1:
Based on RĂ©mi's answer, here's what I did:
I added the following line to the xml config file of the module I wish to create the bean in:
<import resource="classpath:\dsp2aisp-business\*\root-context.xml" />
I also kept the #Autowired annotation on the AccountService (which is the bean I would like to use in this module).
In the other module where the AccountService is declared I added the bean declaration:
<bean id="accountService" class="fr.bnp.dsp2aisp.service.AccountService">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
But I kept getting this error:
Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [fr.bnp.dsp2aisp.service.AccountService] for bean with name 'accountService' defined in ServletContext resource [/WEB-INF/spring/root-context.xml]; nested exception is java.lang.ClassNotFoundException: fr.bnp.dsp2aisp.service.AccountService
But I'm certain that the class does in fact exist in the specified path.
Is there maybe another way to use a bean that is declared in another Maven Module?
I finally got it to work. When you have a multi-moduled Maven project, and you're trying to import one of those modules into another to use its classes, you shouldn't add your module from the build path (properties -> Java Build Path -> Projects ->add) because the compiler won't be able to find those classes in runtime.
What you need to do is to simply add those modules as Maven dependencies in the POM.xml file of the module you wish to use them in.
Example:
<dependency>
<groupId>com.test</groupId>
<artifactId>test-infra</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
The solution is insanely easy but finding it was a little bit tiring. I hope it'll help somebody else with the same problem.
You could do this by importing other maven project bean context form classpath :
<import resource="classpath:path/to/another/maven/module/context.xml" />
Then you can use all the beans defined in other maven project context in your actual maven project

#AspectJ aspects not being weaved in application that uses Spring

I have added the line <context:load-time-weaver/>
to my application-context.xml,
and have created a META-INF\aop.xml file with the following:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver options="-verbose">
<!-- only weave classes in our application-specific packages -->
<include within="com.xxx.aspectj.*"/>
</weaver>
<aspects>
<aspect name="com.xxx.aspectj.StandardAspect"/>
</aspects>
</aspectj>
With the aspect including a catch-all to simply see if it is even working:
#After("call (public * *(..))")
public void interceptEverything() {
System.out.println("Hello");
}
But nothing seems to happen, any ideas?
Removing the include within tag solved my problem.
I realized that I have misinterpreted the include within tag, I thought it had meant only weave aspects in the package listed, but it meant only weave aspects into classes in the package listed.

how to setup load-time weaving with Spring and Tomcat WITHOUT using the -javaagent on the command line

I am using Spring 3.2.9, Tomcat 6.0.44
I am trying to configure my application's Spring instrumentation provider (e.g. spring-instrumentation.jar) for load-time weaving, when it is deployed on Tomcat.
I have a requirement to NOT use:
"-javaagent:/path/path/spring-instrument.jar" on the command line to do the configuration.
I've read that I can configure the Spring instrumentation by modifying the <Context> element of my application's Tomcat configuration (in either Tomcat's server.xml or my web app's context.xml). Adding the appropriate <Context> element to the server.xml results in my application being correctly configured to run with Spring's instrumentation provider. Adding it to the context.xml (see below) does NOT result in a working setup.
I have a META-INF/aop.xml file, looks like this:
<aspectj>
<weaver options="-verbose -showWeaveInfo -debug">
<include within="com.mv.xx.services..*"/>
<exclude within="com.mv.xx.aop..*"/>
</weaver>
<aspects>
<aspect name="com.mv.xx.aop.MyAspect"/>
</aspects>
</aspectj>
I also specify that I want to use load-time weaving by adding this to my Spring context config:
<context:load-time-weaver />
And I add this jar to my application's classpath:
spring-instrument-tomcat.jar
WHAT I HAVE TRIED:
When starting Tomcat, If I identify the location of the spring-instrument.jar on the command line using the -javaagent parameter like this:
-javaagent:/path/path2/spring-instrument-3.2.9.RELEASE.jar
Everything works fine.
Next I removed "-javaagent:/path/path2/spring-instrument-3.2.9.RELEASE.jar" from the command line.
In Tomcat's server.xml file (located in $CATALINE_HOME/conf), I added a a <Context> element to the <Host> element, like this:
<Context path="/myApp" docBase="myApp">
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>
With this configuration everything behaves properly. However I have a requirement to not modify Tomcat's server.xml, since I don't have control over the server.xml (DevOps does, and is reluctant to modify it).
Next I removed the <Context> element from Tomcat's server.xml.
According to the Spring docs, I can add a /META-INF/context.xml to my webapp, and put the <Context> element that used to be in Tomcat's server.xml into the context.xml, like so:
<Context>
<Context path="/myApp" docBase="myApp">
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>
</Context>
However when I restart Tomcat, I get an error message in the logs saying:
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.weaving.AspectJWeavingEnabler#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver': Initialization of bean failed; nested exception is java.lang.IllegalStateException: ClassLoader [org.apache.catalina.loader.WebappClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar
After digging around, I read something that suggested that I modify the <context:load-time-weaver/> element in my Spring config, like this:
<context:load-time-weaver weaver-class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
And add the jar containing InstrumentationLoadTimeWeaver.class to my classpath.
However when I do that, I get this error message in the logs:
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
java.lang.IllegalStateException: Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.
etc....
Can anyone explain how to setup load-time weaving with Spring and Tomcat WITHOUT using the -javaagent on the command line, and WITHOUT modifying the server.xml?
This is the code that I managed to use in order to removed the exception that you mentioned.
Basically you have to implement the LoadTimeWeavingConfigurer and override the method getLoadTimeWeaver().
#Configuration
#ComponentScan(basePackages = "org.myproject")
#EnableAspectJAutoProxy
#EnableSpringConfigured
#EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.AUTODETECT)
public class Config implements LoadTimeWeavingConfigurer {
#Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
#Bean
public InstrumentationLoadTimeWeaver loadTimeWeaver() throws Throwable {
InstrumentationLoadTimeWeaver loadTimeWeaver = new InstrumentationLoadTimeWeaver();
return loadTimeWeaver;
}
}
I used invesdwin-instrument to perform that. It allows you to use Load time weaving instrumentation dynamically so that you don't have to use any javaagent.
It took me a bit of effort to make it work with Tomcat 8.5 though. But it finally work using this configuration with Spring Boot :
#SpringBootApplication
#EnableLoadTimeWeaving // instead of #ImportResource(locations = "classpath:/META-INF/ctx.spring.weaving.xml")
public class MySpringBootApplication {
public static void main(final String[] args) {
DynamicInstrumentationLoader.waitForInitialized(); //dynamically attach java agent to jvm if not already present
DynamicInstrumentationLoader.initLoadTimeWeavingContext(); //weave all classes before they are loaded as beans
SpringApplication.run(MySpringBootApplication.class, args); //start application, load some classes
}
}
It should also work with previous version of Tomcat.
Regards
https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving
Try maven plug-in from the above link. It's working

Spring load time weaving

After 12 hours of trying I don't seem to be able to get Spring load time weaving working on Tomcat.
Spring 4.2.1
Hibernate 4.3.11
Tomcat 8.09
I am trying to get an #Entity autowired.
The weaver output always says:
not weaving 'mypackage.MyEntity'
unless I also use a #Configuration annotation on it. It will then weave but I get back A SPRIGNCGLIB proxy where all the properties are null.
If I remove the #Configuration annotation (I don't think it should be there anyway) then I don't get any weaving and #Autowired property is always null.
This is my configuration:
applicationContext-beans.xml
<context:component-scan base-package="my.package" />
<context:spring-configured />
<context:load-time-weaver />
classes/META-INF/aop.xml
<aspectj>
<weaver options="-Xreweavable">
<include within="my.package.*"/>
</weaver>
<aspects>
<aspect name="org.springframework.beans.factory.aspectj.AbstractInterfaceDrivenDependencyInjectionAspect"/>
</aspects>
MyEntity.java
package my.package;
#Entity
#Table(name = "user")
#Configurable
public class User {
private Encrypter encrypter; // THE CLASS I WANT INJECTED
#Autowired
public void setEncrypter(Encrypter encrypter) {
this.encrypter = encrypter;
}
}
context.xml
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
The Tomcat lib folder has (I am not sure it needs both of these):
spring-instrument-4.2.1.RELEASE.jar
spring-tomcat-weaver-2.5.6.SEC03.jar
The apps WEB-INF/lib folder has:
aspectjweaver-1.8.6.jar
spring-aop-4.2.1.RELEASE.jar
spring-aspects-4.2.1.RELEASE.jar
I have tried starting Tomcat with
-javaagent:D:/my/path/to/server/apache-tomcat-8.0.9/lib/spring-instrument-4.2.1.RELEASE.jar
but it didn't help and according to the Spring LTW documentation the context.xml fragment is the preferred way to do this.
Does anyone have any ideas?

Resources