I'm trying IntelliJ IDEA after many years as an Eclipse user. At the same time, I'm working on a project that I've inherited with many dependencies.
One class will not compile, because IDEA claims that a method in another class does not exist. I can see the method in its source. Control-clicking on the class name in the IDEA editor takes me to the source that looks OK.
My hypothesis is that the compiler isn't using the class compiled from the source within the project, but a class with the same name, somewhere among my dozens of library jars.
How can I find out where IDEA's compiler is finding the clashing class?
CTRL-N and entering the class name should show you all of the matching classes from across the classpath, and which directory/JAR they're in. If there's a clash, you should have duplicates in that list.
Another possibility is that the source you have for the referenced class doesn't match the compiled version of that class.
Related
I'm developing plugin for IntelliJ IDEA. How can plugin get the name and version of libraries that are imported to the project that is being checked by plugin? I have PsiClass of the project, but cannot convert it to java.lang.Class. Maybe there's the way to get ClassLoader from PsiElement?
super.visitImportStatement(psiImport);
Class importedClass = Class.forName(psiImport.getQualifiedName(), true, psiImport.getClass().getClassLoader());
PsiImport.getClass().GetClassLoader() - returns ClassLoader of class PsiImportStatementImpl instead of ClassLoader of class that I've imported.
IntelliJ does mostly static analysis on your code. In fact, the IDE and the projects you run/debug have completely different classpaths. When you open a project, your dependencies are not added to the IDE classpath. Instead, the IDE will index the JARs, meaning it will automatically discover all the declarations (classes, methods, interfaces etc) and save them for later in a cache.
When you write code in your editor, the static analysis tool will leverage the contents of this index to validate your code and show errors when you're trying to use unknown definitions for example.
On the other hand, when you run a Main class from your project, it will spawn a new java process that has its own classpath. This classpath will likely contain every dependency declared in your module.
Knowing this, you should now understand why you can't "transform" a PsiClass to a corresponding Class.
Back to your original question:
How can plugin get the name and version of libraries that are imported to the project that is being checked by plugin?
You don't need to access Class objects for this. Instead, you can use IntelliJ SDK libraries. Here's an example:
Module mod = ModuleUtil.findModuleForFile(virtualFile,myProject);
ModuleRootManager.getInstance(mod).orderEntries().forEachLibrary(library -> {
// do your thing here with `library`
return true;
});
I would like to get deeper understanding on how Java deals with different versions of Classes/Packages/etc., but couldn't find any resources or at least the best way to google for it. The problem is as follows.
Imagine we have some external package com.external.package that contains a definition of SomeInterface.
Now I write a java class MyClass that implements SomeInterface, and using com.external.package v1.0.0. Next I package a (lean) jar containing MyClass.
Now I plug this jar in another program that is looking for implementations of SomeInterface, but in it's dependencies, it is using com.external.package v2.0.0.
Is the reason I get Failed to find any class that implements SomeInterface that versions of SomeInterface don't match in the program and in the jar that contains a class extending it?
Basically the question I would like to find an answer for is what info do jars store regarding external dependencies? Does it store the exact versions of them and if they don't match at the runtime it complains? But why does it even allow running the program with references to same dependency, but different versions?
Is the reason I get Failed to find any class that implements SomeInterface that versions of SomeInterface don't match in the program and in the jar that contains a class extending it?
There is no "versioning" happening here. Simply, the error states no such class exists on the classpath. For example, you didn't put a -cp in your java command to add that extra JAR/class file.
Other reasons this could happen is that an API marks a class as deprecated in v1, then decides to remove it from v2. In which case, you best try to compile and test your code against the proper library versions before you package your own code. If you made an uber JAR, the classes should get shaded, and you probably wouldn't have missing classes.
Maven projects do have the concept of transitive, versioned dependencies, but you've not said anything about that
Seeing that the original question has found an answer already, it seems somewhat relevant to mention that Java Packages and JARs could be used for specifying package version information as discussed in the following documentation:
https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html#wp89936
Also, the Oracle Java Tutorials discuss them and further concepts around deployment of programs as JAR Files as documented here:
https://docs.oracle.com/javase/tutorial/deployment/jar/index.html
I got a project with lots of dependencies and somewhere in my code I'm implementing a class, like this:
public class MyApp extends org.some.BaseClass
Problem is, there are multiple jars in the dependencies providing the base class org.some.BaseClass, some of them containing specific abstract methods that I need to implement, others don't. Obviously, the compiler wants to use one of those base classes that I don't want to implement, and it fails with this:
[ERROR] MyApp is not abstract and does not override abstract method getSome() in org.some.BaseClass
Now, from here: Find where java class is loaded from I learned about the jvm opt -verbose:class which lists all the classes that are being loaded and where it loads them from. This seems to be really nice for analyzation purposes. However, I guess since the compiler is still at work at that time and the classes have not yet been loaded fully, the source of org.some.BaseClass is not printed in the output. The compiler just fails and doesn't mention any details.
So, how can I find out what class is being compiled against at compile time? Is there another jvm flag that prints the same infos as verbose:class for files with compilation errors?
Update:
Solved by adding -verbose to the javac args. Turned out the wrong class came from a shaded jar that directly included all the classes from its dependencies. The IDE was unable to tell me about this, because that jar was also part of the project as a module.
From the javac manual:
-verbose - Verbose output. This includes information about each class loaded and each source file compiled.
I was using JD-GUI to get readable content of the several .class files in order to create a custom one, because many parts of the original libraries are not used (save space and performance)
So I read the code and started creating an eclipse library project in eclipse pasting them in.
soon apeared the first weird errors:
The method getLogger(Class) from the type Logger refers to the missing type Class
Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor
and so on.
Does anyone has experience in creating libraries from other libraries in eclipse? what is going wrong?
thank you.
EDIT:
it seems that several imports are not found:
error example:
The import org.w3c.dom.stylesheets cannot be resolved
how can this be, when the original libraries are working fine? I did not remove a single class. my custom library is just a merged one with 100% the same content.
I have a project with different classes and packages as dependencies. Note that everything writte below occurs in one project.
I have a class that at some point runs the code getDiagramPanel().setRelationsPaintOrder(new Comparator() {.
getDiagramPanel() calls the method from DjtSheet.class, which is located in a dependency .jar-file. This method returns the DjtDiagramPanel object. I also have a DjtDiagramPanel.java file, which should override the one from the package and contains the method setRelationsPaintOrder().
In Java 7, this works fine. It correctly calls the method from the dependency, which returns the object in the format of the class which overrides the panelclass from the dependency package.
In Java 6 however, the panelclass from the dependency package is returned instead of the one from my project.
java.lang.NoSuchMethodError:
com.dlsc.djt.gantt.DjtDiagramPanel.setRelationsPaintOrder(Ljava/util/Comparator;)V
Note that this message occurs at runtime! Compiling the project gives no errors.
How can I solve this?
This problem definitely means that you have a problem in class path. I guess that the problem is that class DjtDiagramPanel is duplicate and you have 2 different veraions: one that has method setRelationsPaintOrder and second that does not have. Apparently you compile code against the "good" version and run against the "bad" one.
When this happens you can probably change the order of class loading by playing with order of dependencies in project properties of eclipse, but it will just fail later (on production). So, you should find what is the root cause of the duplication.
First find these 2 versions of the same class. Then find how the bad version arrived to your classpath. It typically happes because of 3rd party dependencies. If you are using maven you can use dependency plugin to find the root cause and disable it using tag "exclusion".