Intellij Plugin - Load class object from source project

Hi, 

I am trying to develop an Intellij Plugin, which could generate Json Schema out of a Java Class using this awesome library:

https://github.com/FasterXML/jackson-module-jsonSchema 

To use this library, the class object is needed, like 

JsonSchema schema = schemaGen.generateSchema(SimpleBean.class);

However, I cannot load the class object from my source code in my plugin. My code shows below:

JsonSchema schema = schemaGen.generateSchema(ClassLoader.getSystemClassLoader().load(psiClass.getName()));

Is there anyway to achieve to load class object from source code into plugin? 

14 comments
Comment actions Permalink

Hi,

ClassLoader loads classes from *.class files from directories which are added to its classpath. Classpath of ClassLoader.getSystemClassLoader consists of paths to the IDE JARs, not directories with classes from project opened in the IDE. Also until you build the project where are no *.class files for the source files at all.

So if you want to use this method, you firstly need to build the project, see CompilerManager's javadoc for details. After that you need to create a ClassLoader (e.g. IDEA's com.intellij.util.lang.UrlClassLoader) instance and pass to it URL of the module output directory which contains compiled classes (com.intellij.openapi.roots.CompilerModuleExtension#getCompilerOutputUrl). And then load the class using that class loader (but please note that PsiClass::getName returns short name of the class, you need to use PsiClass::getQualifiedName).

1
Comment actions Permalink

 

Thank you, Nikolay. It really solved the problem.

A following question would be that I found if I load the *.class using UrlClassLoader, then the @JsonProperty, @Size, and other annotations will not be detected(or I should say "parsed") when the library generating the Json schema.

Do you have any suggestion? Thank you.

0
Comment actions Permalink

You need to add the JARs containing these annotations to classpath as well. You can use OrderEnumerator class to get classpath of your module (something like OrderEnumerator.orderEntries(module).recursively().classes().getUrls()).

0
Comment actions Permalink

Thank you, Nikolay.

I checked that the dependency libraries have been already included in the Plugin project.

In order to test and compare, I create this SimpleBean1.class in my Plugin project, and use the same statement:

schemaGen.generateSchema(SimpleBean1.class);

 it is working fine with the annotations.

However, when I load the SimpleBean2.class(same content with SimpleBean1) from other project into my Plugin project. The output json schema seems never considered/detected the annotations.

0
Comment actions Permalink

I meant that you need to add the JAR containing annotations to the classpath of UrlClassLoader you create.

0
Comment actions Permalink

Just want to clarify,

String simpleBeanClassPath = getSourceClassPath(); // somehow i get the SimpleBean.class path string
ClassLoader classLoader = getClassLoader(); // somehow i create the urlClassLoader of the source code

// Need to add in JAR

Class<?> simpleBeanClass = classLoader.loadClass(simpleBeanClassPath);
JsonNode node = schemaGen.generateSchema(simpleBeanClass);

Do you mean that before I load the SimpleBean.class, I need to add the JAR to my classLoader? Is it in the "// Need to add in JAR" step, or somewhere in the getClassLoader()?

0
Comment actions Permalink

I have tried to use

String[] jars = OrderEnumerator.orderEntries(psiClass.getProject()).recursively().classes().getUrls();

and then use jars as urls when create the urlClassLoader in the function getClassLoader()

However, i got the same output for json node.

0
Comment actions Permalink

How do you convert String to URL? Look how it's done in our code. Also you can debug the code and try loading annotations classes via the created ClassLoader in debugger to check whether the classpath is configured properly.

0
Comment actions Permalink

Thank you, Nikolay.

The class loading problem has been solved by following the same way in your code.

However, when i try to build the source code using CompileManager, i get java.io.FileNotFoundException: /Applications/IntelliJ IDEA.app/Contents/system/index/indices.enum (No such file or directory)

I checked that it seems there's no system/ directory in /Applications/IntelliJ IDEA.app/Contents

Do you have any suggestion?

0
Comment actions Permalink

I suggest to post the full stacktrace here.

0
Comment actions Permalink

Error:Internal error: (java.lang.RuntimeException) java.io.FileNotFoundException: /Applications/IntelliJ IDEA.app/Contents/system/index/indices.enum (No such file or directory)
java.lang.RuntimeException: java.io.FileNotFoundException: /Applications/IntelliJ IDEA.app/Contents/system/index/indices.enum (No such file or directory)
at com.intellij.util.indexing.ID.writeEnumFile(ID.java:139)
at com.intellij.util.indexing.ID.<clinit>(ID.java:75)
at org.jetbrains.jps.backwardRefs.index.CompilerIndices.<clinit>(CompilerIndices.java:43)
at org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex.<init>(CompilerBackwardReferenceIndex.java:85)
at org.jetbrains.jps.backwardRefs.BackwardReferenceIndexWriter.initialize(BackwardReferenceIndexWriter.java:98)
at org.jetbrains.jps.backwardRefs.BackwardReferenceIndexBuilder.buildStarted(BackwardReferenceIndexBuilder.java:53)
at org.jetbrains.jps.incremental.IncProjectBuilder.runBuild(IncProjectBuilder.java:372)
at org.jetbrains.jps.incremental.IncProjectBuilder.build(IncProjectBuilder.java:192)
at org.jetbrains.jps.cmdline.BuildRunner.runBuild(BuildRunner.java:138)
at org.jetbrains.jps.cmdline.BuildSession.runBuild(BuildSession.java:295)
at org.jetbrains.jps.cmdline.BuildSession.run(BuildSession.java:125)
at org.jetbrains.jps.cmdline.BuildMain$MyMessageHandler.lambda$channelRead0$0(BuildMain.java:236)
at org.jetbrains.jps.service.impl.SharedThreadPoolImpl.lambda$executeOnPooledThread$0(SharedThreadPoolImpl.java:42)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.FileNotFoundException: /Applications/IntelliJ IDEA.app/Contents/system/index/indices.enum (No such file or directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
at java.io.FileOutputStream.<init>(FileOutputStream.java:162)
at java.io.FileWriter.<init>(FileWriter.java:90)
at com.intellij.util.indexing.ID.writeEnumFile(ID.java:117)
... 17 more

0
Comment actions Permalink

Hello,

what version of IDEA do you use? As I see from the source code you're using 2017.1, so if yes than please update the IDE.

0
Comment actions Permalink

Thank you, Dmitry.

Problem was solved by updating to v15.0.6

Thank you guys so much.

0
Comment actions Permalink

But actual production version of IntelliJ IDEA is 2017.2 and 2017.3 will be available soon. So, I suggest to use at least 2017.2.

0

Please sign in to leave a comment.