Spring Boot loads component before its dependant bean - java

I have a weird behavior in my project using Spring. I have this structure:
Main-Spring-Project
|_Depends on Library A
|_Depends on Library B
Now... there is an #Autowired in a component in A which injects a component which is in B.
When I have all 3 projects opened in my development environment and start de main project, everythin works fine. But if I remove the project A and leave B, it all crashes when starting saying:
Field factory in [class in A] required a bean of type '[class in B]' that could not be found.
so... I suspect it is loading A, before B.
What I don't understand is why it works perfect if I have both projects open, and why it crashes when I have A closed (main project is using its JAR)
By the way... if I remove A and B, everything works perfect again.

No Spring doesn't make a mistake during the context loading.
This behavior makes sense : the class is just not available at runtime.
Here you work with snapshot/current development source code/compiled classes of library B since library B makes part of your current development code.
It means that the dependencies/classes of library B needed during the run of the application doesn't depend on Maven for retrieving them but for what your IDE has access : the library A and B projects.
To avoid that you should install (mvn clean install) the B dependency in your local Maven/Gradle repository but that may be not practical if you need to repeat the task 20 days a day because the B source code changes 20 days a day.
Note that if you didn't use your IDE, you would be constraint to install the dependency at each modification. So finally, it is not so bad at all. No ?

Related

How to solve circular dependency in gradle multi-project build

Consider the following situation. I have two gradle (sub-)projects called "A" and "B". A defines some classes/interfaces that are being referenced by B. So B has a compile dependency to A. Now A is a web server that should be started with B on the classpath. How do you achieve that with gradle?
Of course it is not possible to add B as compile dependency to A because that would mean a circular dependency between A and B. Even adding B as runtime dependency to A did not work because then compile errors in B state that referenced classes from A do not exist. But why?
One solution would be to move code from B into A but I really would like to separate that code because there might be another implementation of B later that I want to swap easily in A (e.g. by exchanging the jar in runtime classpath).
Another solution I was thinking about is to separate classes from A referenced by B into a new module and make both A and B depend on that new module. This sounds valid but that would imply to move persistence layer from A to that new module which feels wrong.
Additional information: A is a Spring boot web application with persistence layer, web services etc, B produces a JAR.
Circular dependencies are a well-known problem when you try to get Dependency Injection. In this case, you have something similar but at a module level
The only way I see you can solve your issue is by creating a third module C with the common code (probably the A interfaces referenced by B)
This way you can compile C (it doesn't have any dependencies), A (it depends on C), and B (it depends on C) and launch A with B in its classpath
Everytime you end up with circular dependency you probably should introduce another entity to break the cycle.
Have a look at my explanation in this other QA article (it's dealing with packages and classes, but idea is the same): What does it mean and how to fix SonarQube Java issue "Cycles between packages should be removed" (squid:CycleBetweenPackages)

How we can get under development dependency in maven

I encounter an issue that, I have 3 projects porject_1,porject_2 and porject_3.
porject_1 contains one abstract class for example ClassDef.java, and porject_2 and porject_3 are implementing ClassDef.java in there code some where.
If ClassDef.java has added some new API in porject_1(under development means not released yet ) which should be verified by porject_2 & porject_3.
But in maven porject_2 & porject_3 need porject_1 dependency to get new implementation and we can not add porject_1 development jar into class path of porject_2 & porject_3 project in maven hence I can't take latest change in both dependent project at same time. how could I resolve this issue?

NoSuchMethod Error in Java-Solved: Multiple projects added in POM.xml as dependencies had same class name and same method. Renamed them

I have a listener program that triggers a main program based on the below conditions
If the database has new records
If the time from last run is 10 minutes.
This is done using ScheduledExecutorService of Java (https://stackoverflow.com/a/26043250/1930402 helped out).
So, I am calling a perfectly working program from this Listener, located in another Project and another Folder. That is, I am calling project B from project A. To enable this, I added a dependency of Project B in Project A's POM.xml.
There is an error in one of the classes of Project B, and it is NoSuchMethodError. I can see the method right there. From the answers of this post, https://stackoverflow.com/a/24902026/1930402, I understand that this particular class might not be there in my build path. I am not sure of this.
Can anyone advise?
Note: Project B runs without any issues when it is run independently.
Java Doc tells:
"Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed."
How can the definition of my class change while running using a static instance?
This is my stack:
java.lang.NoSuchMethodError:a.b.c.StringUtils.formatDateToString(Ljava/lang/Object;)Ljava/lang/String;
So, these are the key points: Maven Dependency, ScheduledExecutorService, Static Class.
The reason for the error was that same class names and same package names were present in Project A and Project B and a set of other projects which were being used. Once I made each of them unique, things started working fine.

ActivityInstrumentationTestCase2.getActivity() throws ClassCastException for libraries

I am writing Android JUnit test cases for Project A which has Project B as a library (Project Properties / Android / Library). My test cases need to access resources (view ids, strings, etc.) from both Project A and Project B. If I add Project A and/or Project B as Android / Libraries or as Java Build Path / Projects to my test project, any call to ActivityInstrumentationTestCase2.getActivity() throws a ClassCastException.
If I don't add them, the call returns the appropriate Activity, but I don't have access to the resource ids of the two projects under test. I also don't have access to the objects in Project B, which are needed to properly test Project A. I can't use mock objects here.
Has anybody encountered and resolved this before?
Try this to access resources from the library: (I think it will work)
getInstrumentation().getTargetContext().getResources()...;
This will load resources from the test project: (definitely works)
getInstrumentation().getContext().getResources()...;

What causes java.lang.IncompatibleClassChangeError?

I'm packaging a Java library as a JAR, and it's throwing many java.lang.IncompatibleClassChangeErrors when I try to invoke methods from it. These errors seem to appear at random. What kinds of problems could be causing this error?
This means that you have made some incompatible binary changes to the library without recompiling the client code. Java Language Specification §13 details all such changes, most prominently, changing non-static non-private fields/methods to be static or vice versa.
Recompile the client code against the new library, and you should be good to go.
UPDATE: If you publish a public library, you should avoid making incompatible binary changes as much as possible to preserve what's known as "binary backward compatibility". Updating dependency jars alone ideally shouldn't break the application or the build. If you do have to break binary backward compatibility, it's recommended to increase the major version number (e.g. from 1.x.y to 2.0.0) before releasing the change.
Your newly packaged library is not backward binary compatible (BC) with old version. For this reason some of the library clients that are not recompiled may throw the exception.
This is a complete list of changes in Java library API that may cause clients built with an old version of the library to throw java.lang.IncompatibleClassChangeError if they run on a new one (i.e. breaking BC):
Non-final field become static,
Non-constant field become non-static,
Class become interface,
Interface become class,
if you add a new field to class/interface (or add new super-class/super-interface) then a static field from a super-interface of a client class C may hide an added field (with the same name) inherited from the super-class of C (very rare case).
Note: There are many other exceptions caused by other incompatible changes: NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, VerifyError, NoClassDefFoundError and AbstractMethodError.
The better paper about BC is "Evolving Java-based APIs 2: Achieving API Binary Compatibility" written by Jim des Rivières.
There are also some automatic tools to detect such changes:
japi-compliance-checker
clirr
japitools
sigtest
japi-checker
Usage of japi-compliance-checker for your library:
japi-compliance-checker OLD.jar NEW.jar
Usage of clirr tool:
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
Good luck!
While these answers are all correct, resolving the problem is often more difficult. It's generally the result of two mildly different versions of the same dependency on the classpath, and is almost always caused by either a different superclass than was originally compiled against being on the classpath or some import of the transitive closure being different, but generally at class instantiation and constructor invocation. (After successful class loading and ctor invocation, you'll get NoSuchMethodException or whatnot.)
If the behavior appears random, it's likely the result of a multithreaded program classloading different transitive dependencies based on what code got hit first.
To resolve these, try launching the VM with -verbose as an argument, then look at the classes that were being loaded when the exception occurs. You should see some surprising information. For instance, having multiple copies of the same dependency and versions you never expected or would have accepted if you knew they were being included.
Resolving duplicate jars with Maven is best done with a combination of the maven-dependency-plugin and maven-enforcer-plugin under Maven (or SBT's Dependency Graph Plugin, then adding those jars to a section of your top-level POM or as imported dependency elements in SBT (to remove those dependencies).
Good luck!
I have also discovered that, when using JNI, invoking a Java method from C++, if you pass parameters to the invoked Java method in the wrong order, you will get this error when you attempt to use the parameters inside the called method (because they won't be the right type). I was initially taken aback that JNI does not do this checking for you as part of the class signature checking when you invoke the method, but I assume they don't do this kind of checking because you may be passing polymorphic parameters and they have to assume you know what you are doing.
Example C++ JNI Code:
void invokeFooDoSomething() {
jobject javaFred = FredFactory::getFred(); // Get a Fred jobject
jobject javaFoo = FooFactory::getFoo(); // Get a Foo jobject
jobject javaBar = FooFactory::getBar(); // Get a Bar jobject
jmethodID methodID = getDoSomethingMethodId() // Get the JNI Method ID
jniEnv->CallVoidMethod(javaFoo,
methodID,
javaFred, // Woops! I switched the Fred and Bar parameters!
javaBar);
// << Insert error handling code here to discover the JNI Exception >>
// ... This is where the IncompatibleClassChangeError will show up.
}
Example Java Code:
class Bar { ... }
class Fred {
public int size() { ... }
}
class Foo {
public void doSomething(Fred aFred, Bar anotherObject) {
if (name.size() > 0) { // Will throw a cryptic java.lang.IncompatibleClassChangeError
// Do some stuff...
}
}
}
I had the same issue, and later I figured out that I am running the application on Java version 1.4 while the application is compiled on version 6.
Actually, the reason was of having a duplicate library, one is located within the classpath and the other one is included inside a jar file that is located within the classpath.
In my case the error appeared when I added the com.nimbusds library in my application deployed on Websphere 8.5.
The below exception occurred:
Caused by: java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor
The solution was to exclude the asm jar from the library:
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>5.1</version>
<exclusions>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
</exclusions>
</dependency>
Another situation where this error can appear is with Emma Code Coverage.
This happens when assigning an Object to an interface. I guess this has something to do with the Object being instrumented and not binary compatible anymore.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
Fortunately this problem doesn't happen with Cobertura, so I've added cobertura-maven-plugin in my reporting plugins of my pom.xml
I've faced this issue while undeploying and redeploying a war with glassfish. My class structure was like this,
public interface A{
}
public class AImpl implements A{
}
and it was changed to
public abstract class A{
}
public class AImpl extends A{
}
After stopping and restarting the domain, it worked out fine.
I was using glassfish 3.1.43
All of the above - for whatever reason I was doing some big refactor and starting to get this. I renamed the package my interface was in and that cleared it. Hope that helps.
I have a web application that deploys perfectly fine on my local machine's tomcat(8.0.20). However, when I put it into the qa environment (tomcat - 8.0.20), it kept on giving me the IncompatibleClassChangeError and it was complaining that I was extending on an interface. This interface was changed to an abstract class. And I compiled the parent and child classes and still I kept on getting the same issue. Finally, I wanted to debug, so, I changed the version on the parent to x.0.1-SNAPSHOT and then compiled everything and now it is working. If someone is still hitting the problem after following the answers given here, please make sure the versions in your pom.xml are also correct. Change the versions to see if that works. If so, then fix the version problem.
My answer, I believe, will be Intellij specific.
I had rebuilt clean, even going as far as to manually delete the "out" and "target" dirs. Intellij has a "invalidate caches and restart", which sometimes clears odd errors. This time it didn't work. The dependency versions all looked correct in the project settings->modules menu.
The final answer was to manually delete my problem dependency from my local maven repo. An old version of bouncycastle was the culprit(I knew I had just changed versions and that would be the problem) and although the old version showed up no where in what was being built, it solved my problem. I was using intellij version 14 and then upgraded to 15 during this process.
In my case, I ran into this error this way. pom.xml of my project defined two dependencies A and B. And both A and B defined dependency on same artifact (call it C) but different versions of it (C.1 and C.2). When this happens, for each class in C maven can only select one version of the class from the two versions (while building an uber-jar). It will select the "nearest" version based on its dependency mediation rules and will output a warning "We have a duplicate class..." If a method/class signature changes between the versions, it can cause a java.lang.IncompatibleClassChangeError exception if the incorrect version is used at runtime.
Advanced: If A must use v1 of C and B must use v2 of C, then we must relocate C in A and B's poms to avoid class conflict (we have a duplicate class warning) when building the final project that depends on both A and B.
An additional cause of this issue, is if you have Instant Run enabled for Android Studio.
The fix
If you find you start getting this error, turn off Instant Run.
Android Studio main settings
Build, Execution, Deployment
Instant Run
Untick "Enable instant run..."
Why
Instant Run modifies a large number of things during development, to make it quicker to provide updates to your running App. Hence instant run. When it works, it is really useful. However, when an issue such as this strikes, the best thing to do is to turn off Instant Run until the next version of Android Studio releases.
Please check if your code doesnt consist of two module projects that have the same classes names and packages definition. For example this could happen if someone uses copy-paste to create new implementation of interface based on previous implementation.
If this is a record of possible occurences of this error then:
I just got this error on WAS (8.5.0.1), during the CXF (2.6.0) loading of the spring (3.1.1_release) configuration where a BeanInstantiationException rolled up a CXF ExtensionException, rolling up a IncompatibleClassChangeError. The following snippet shows the gist of the stack trace:
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.apache.cxf.bus.spring.SpringBus]: Constructor threw exception; nested exception is org.apache.cxf.bus.extension.ExtensionException
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:162)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:76)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:990)
... 116 more
Caused by: org.apache.cxf.bus.extension.ExtensionException
at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:167)
at org.apache.cxf.bus.extension.Extension.getClassObject(Extension.java:179)
at org.apache.cxf.bus.extension.ExtensionManagerImpl.activateAllByType(ExtensionManagerImpl.java:138)
at org.apache.cxf.bus.extension.ExtensionManagerBus.<init>(ExtensionManagerBus.java:131)
[etc...]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
... 118 more
Caused by: java.lang.IncompatibleClassChangeError:
org.apache.neethi.AssertionBuilderFactory
at java.lang.ClassLoader.defineClassImpl(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:284)
[etc...]
at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:586)
at java.lang.ClassLoader.loadClass(ClassLoader.java:658)
at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:163)
... 128 more
In this case, the solution was to change the classpath order of the module in my war file. That is, open up the war application in the WAS console under and select the client module(s). In the module configuration, set the class-loading to be "parent last".
This is found in the WAS console:
Applicatoins -> Application Types -> WebSphere Enterprise Applications
Click link representing your application (war)
Click "Manage Modules" under "Modules" section
Click link for the underlying module(s)
Change "Class loader order" to be "(parent last)".
Documenting another scenario after burning way too much time.
Make sure you don't have a dependency jar that has a class with an EJB annotation on it.
We had a common jar file that had an #local annotation. That class was later moved out of that common project and into our main ejb jar project. Our ejb jar and our common jar are both bundled within an ear. The version of our common jar dependency was not updated. Thus 2 classes trying to be something with incompatible changes.
For some reason the same exception is also thrown when using JNI and passing the jclass argument instead of the jobject when calling a Call*Method().
This is similar to the answer from Ogre Psalm33.
void example(JNIEnv *env, jobject inJavaList) {
jclass class_List = env->FindClass("java/util/List");
jmethodID method_size = env->GetMethodID(class_List, "size", "()I");
long size = env->CallIntMethod(class_List, method_size); // should be passing 'inJavaList' instead of 'class_List'
std::cout << "LIST SIZE " << size << std::endl;
}
I know it is a bit late to answer this question 5 years after being asked but this is one of the top hits when searching for java.lang.IncompatibleClassChangeError so I wanted to document this special case.
Adding my 2 cents .If you are using scala and sbt and scala-logging as dependency ;then this can happen because scala-logging's earlier version had the name scala-logging-api.So;essentially the dependency resolutions do not happen because of different names leading to runtime errors while launching the scala application.
I got this error because I had an abstract base class which promised that it implements a certain interface, but I had forgotten to add the implementations of the interface methods, and then I created a non-abstract (concrete) bytecode-generated class which extended the abstract class, without providing implementations for those methods, either.
When I tried to create an instance the bytecode-generated class, the JVM complained with java.lang.IncompatibleClassChangeError.
Luckily, the exception has a "message" member which provides more detailed information as to what went wrong. In my case the message clearly said that the particular class was supposed to implement the particular interface, but it did not actually implement it.
If you came from android development. Then give a try of rebuild option might be fix for you.
In my case:
I have a project containing a few modules, including app, test, integrationTest
I created OneElementCache in app module.
Then, I created a file Cache in test module, the file contains some helpers for creating OneElementCache in tests.
Until now, everything works perfectly (both test and integrationTest passes).
After that, I created a file Cache in app module.
Got while running integrationTest:
Caused by: java.lang.IncompatibleClassChangeError:
class app.cache.CacheImpl can not implement app.cache.Cache, because it is not an interface (app.cache.Cache is in unnamed module of loader 'app')
The reason was a conflict in naming in different modules (app/test). Changing the filename in test did the job.

Categories

Resources