Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when creating ScanResult from JSON #682

Closed
thibaultmeyer opened this issue May 24, 2022 · 3 comments
Closed

Error when creating ScanResult from JSON #682

thibaultmeyer opened this issue May 24, 2022 · 3 comments

Comments

@thibaultmeyer
Copy link

thibaultmeyer commented May 24, 2022

Hello,

I get an error when I try to restore a ScanResult from JSON.

Environment

ClassGraph version : 4.8.146

openjdk version "17.0.2" 2022-01-18
OpenJDK Runtime Environment (build 17.0.2+8-86)
OpenJDK 64-Bit Server VM (build 17.0.2+8-86, mixed mode, sharing)

Creating JSON

new ClassGraph()
    .acceptPackages("*")
    .addClassLoader(classLoader)
    .enableAnnotationInfo()
    .enableMethodInfo()
    .scan()
    .toJSON()

Loading JSON

ScanResult.fromJSON(content);

Error

Exception in thread "main" java.lang.IllegalArgumentException: Class io.github.classgraph.Classfile$MethodTypeAnnotationDecorator does not have an accessible default (no-arg) constructor
	at nonapi.io.github.classgraph.json.ClassFieldCache.getDefaultConstructorForConcreteTypeOf(ClassFieldCache.java:215)
	at nonapi.io.github.classgraph.json.JSONDeserializer.populateObjectFromJsonObject(JSONDeserializer.java:371)
	at nonapi.io.github.classgraph.json.JSONDeserializer.populateObjectFromJsonObject(JSONDeserializer.java:601)
	at nonapi.io.github.classgraph.json.JSONDeserializer.populateObjectFromJsonObject(JSONDeserializer.java:601)
	at nonapi.io.github.classgraph.json.JSONDeserializer.populateObjectFromJsonObject(JSONDeserializer.java:601)
	at nonapi.io.github.classgraph.json.JSONDeserializer.populateObjectFromJsonObject(JSONDeserializer.java:601)
	at nonapi.io.github.classgraph.json.JSONDeserializer.populateObjectFromJsonObject(JSONDeserializer.java:601)
	at nonapi.io.github.classgraph.json.JSONDeserializer.deserializeObject(JSONDeserializer.java:680)
	at nonapi.io.github.classgraph.json.JSONDeserializer.deserializeObject(JSONDeserializer.java:707)
	at io.github.classgraph.ScanResult.fromJSON(ScanResult.java:1449)
@lukehutch
Copy link
Member

Hi @thibaultmeyer, thanks for the bug report. I solved the issue of an exception being thrown in this case, however the fix is a bit simplistic.

What is going on here is that type signatures are only parsed lazily as they are requested by the user after the scan is complete (to speed up scanning). Type annotations are added to type signatures only when the type signature is parsed, using "decorator" lambdas. These decorators were added recently (with type annotation support), and they were not marked as transient fields, so the serializer was trying to serialize them and failing. I marked them as transient so that they are skipped over. But the effect of this is that if the user serializes a ScanResult before they have requested type signatures or type descriptors of every object they care about knowing type annotations for, the type annotation information will be lost.

Type annotations are only rarely used, and the built-in JSON serialization functionality is used even more rarely, so I assume that in practice this won't cause a problem for many people. The solution would be to have the serializer run the decorators on every type signature in the ScanResult, but that will require some special-case code in the serializer.

I'm actually going to remove the JSON serializer in ClassGraph 5.0, because it has been a source of some bugs in the past, and almost nobody uses it. Also almost nobody needs the entire ScanResult (which can be huge), they just need some subset of the data. The best practice to replace this with is to manually serialize just the information from the ScanResult that you care about.

The above "fix" is released in ClassGraph 4.8.147. (I put fix in quotes because of the caveat that type annotation information will be lost during serialization.)

@thibaultmeyer
Copy link
Author

Hi @lukehutch,

Thank you for your answer.

@lukehutch
Copy link
Member

lukehutch commented May 25, 2022

@thibaultmeyer The fields in ScanResult and its connected objects (ClassInfo, MethodInfo, FieldInfo, etc.) are actually correctly marked with transitive if they shouldn't be serialized, so you can probably just use Jackson to serialize the object graph to JSON. I would be curious as to how well this works, if you're willing to try it. There are tons of Jackson tutorials on the Web that you can follow to test this.

However one of the reasons why I wrote a custom serializer for ScanResult is that the object graph contains cycles (e.g. ClassInfo contains links to its MethodInfo objects, but also vice versa) -- so the serializer output utilizes a custom object reference system to ensure that the graph can be properly reconstructed. I don't think Jackson handled cycles at the time I wrote ClassGraph, but maybe it does now.

What I was talking about when I said to try custom-serializing to disk just the information you need is simply that you can think about the exact queries you need to run, then run those queries (potentially at build time), and come up with your own way of serializing just those results. For example, if you need to know which classes have a certain class annotation, run the query and then write out to a file just the names of the classes that have that annotation. You can repeat this process for each query type whose results you care about. The result will be dramatically smaller (and therefore faster to read at runtime) than using the full JSON file generated by ClassGraph's built-in serializer.

Also, you should be able to use the built-in serializer again as of ClassGraph-4.8.147, and the 4.x series will continue to work (albeit not receiving further bugfixes) after I release ClassGraph-5.x. Anyway ClassGraph-5.x is not likely to be released before the end of the year, I have too much else going on...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants