Skip to content
This repository was archived by the owner on Feb 12, 2022. It is now read-only.

Inconsistent code generation #316

Closed
glever opened this issue Nov 4, 2017 · 13 comments
Closed

Inconsistent code generation #316

glever opened this issue Nov 4, 2017 · 13 comments
Assignees

Comments

@glever
Copy link

glever commented Nov 4, 2017

I was using the gradle plugin which I built myself (see my comments issue 287 ). And suddenly, without any change on my project structure or codebase, I got a compiler error "ResponseDelegate is not an abstract class and does not implement method getEntity() of javax.ws.rs.core.Response". I also saw that the generated code in model classes was in a different order and imports changed from wildcard static imports to absolute.
I thought it was a local issue on my machine so I wrote a gradle buildscript that downloads and runs the raml-to-jarxrs-jar-with-dependencies.jar . First it seemed the issue was resolved but then I saw it was a random issue. Re-running the tool sometimes generated correct sources, sometimes not.

The only "special" setup I have is that I run java 9.
FWIW, any time I see such inexplicable behaviour I suspect a classloading issue with multiple versions of the same class on the runtime classpath.

Please find attached 2 zips generated right after each other. One time the code doesnt compile, other time it does.
You can find my project at the current revision here .
The raml project is /rest-service/api
the raml api is raml/api.raml and the sources are generated to src/main/generated/raml
Just run gradlew raml2jaxrs

api-bad-gen.zip.zip
api-good-gen.zip

@jpbelang
Copy link
Contributor

jpbelang commented Nov 5, 2017

The ResponseDelegate is generated to implement jaxrs Response. This is calculated introspectively: I look at the Response object on the current classpath and generate the delegator based on that object.

There seems to be some conflict on the classpath between two different versions of jaxrs, but for the life of me, I can't find a jaxrs version that doesn't have getEntity() on the Response object (or why I would skip it's implementation. I'm going to try your big project and see how that fails. You say it fails randomly ?

@jpbelang
Copy link
Contributor

jpbelang commented Nov 5, 2017

The gradle build fails with your project. I have little experience with Kotlin (Intellij is also having trouble loading the project):
parallel execution with configuration on demand is an incubating feature.
e: /Users/jpbelang/IdeaProjects/xmlers/xml/src/main/kotlin/be/glever/xmlers/xml/builder/ArchiveTimestampChainBuilder.kt: (20, 8): Unresolved reference: ietf
e: /Users/jpbelang/IdeaProjects/xmlers/xml/src/main/kotlin/be/glever/xmlers/xml/builder/ArchiveTimestampChainBuilder.kt: (21, 8): Unresolved reference: ietf
...

@glever
Copy link
Author

glever commented Nov 5, 2017

Yes, my apologies. I left you a bit lost with my project structure (however, loading with intellij shouldn't pose any problems, just run 'gradlew' from the top level structure.
Normally only the api.raml and raml-to-jaxrs-with-dependencies.jar should be enough to reproduce.
I'll see if I cannot provide you with a more condensed example.
And I noticed today that gradle incorporates a "tool" in the classpath, so running a jar using my approach may have polluted the runtime classpath of the runnable jar.

@glever
Copy link
Author

glever commented Nov 5, 2017

Ok I was able to reproduce, so it wasn't the project setup (which I admit is a bit 'experimental', I'm also learning kotlin and gradle).

Use attached file: raml.zip , it contains a raml project structure created and edited by the atom 'raml workbench' tool. The zip file also contains the generated output. You'll see the results of 2 consecutive runs under
\src\main\generated\raml\be\glever\xmlers\rest\api\generated\support .

Just place the runnable jar-with-dependencies from search.maven.org inside the unzipped directory and run the command in run.bat .

This is my system info:
win 10 64 bit

java -version:
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)

@jpbelang
Copy link
Contributor

jpbelang commented Nov 5, 2017

java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)

Ran this 30 times:

rm src/main/generated/raml/be/glever/xmlers/rest/api/generated/support/ResponseDelegate.java && sh run.bat && grep 'getEntity(' src/main/generated/raml/be/glever/xmlers/rest/api/generated/support/ResponseDelegate.java

Came up fine all the time.

Could you update to the java GA release ? (You seem to be on an EA release).

@glever
Copy link
Author

glever commented Nov 5, 2017

Removed all traces from java from my machine + cleaned up my path, made sure java was unrecognized.
Downloaded and installed latest jdk 9 -> same issue
Uninstalled, downloaded and installed jdk8 -> same issue.

I'll validate on my workpc tomorrow.

I'll be happy to debug on my machine, I assume I can just import the project in intellij and run the main class in debug mode? Could you point me to a good starting point for setting breakpoints?

@jpbelang
Copy link
Contributor

jpbelang commented Nov 5, 2017

I'm really stunned. It seems to fail ~50% on your side, correct ?

As to the debugging, this is the class generating the code:

org/raml/jaxrs/generator/ResponseSupport.java:79

is paydirt. This is precisely where I add the code. The loop goes over Response.class.getDeclaredMethods(). I suppose if getEntity() isn't in this list, then I won't write it.

This piece of code was added, if memory serves me right, to handle collections properly.

This is the reference to the main().

org.raml.jaxrs.ramltojaxrs.Main#main. Pass the same arguments. Keep me posted. If you need support, ping here.

@glever
Copy link
Author

glever commented Nov 5, 2017

I was hitting the conditional breakpoint ("ResponseDelegate".equals(this.name);) on the javapoet TypeSpec constructor a lot of times, then it hit me:
==>
ResponseSupport:105-106 (creation of the java file) is inside the foreach(method) loop.
Shouldn't this be outside? If this is the case I think we found our answer :)

Code was generating the class for each method it added, endresult should still have been consistent like on your machine, but initial inspection of the javapoet sources I dont see a try/finally block closing the resource but this could be hidden behind some abstractions.
If this is the case this could explain inconsistent results on different systems (file write race conditions).

I moved
JavaFile.Builder file = JavaFile.builder(defaultPackage, builder.build()); file.build().writeTo(rootDir);
outside the loop and it seems the ResponseDelegate now always retains the getEntity() method. However, the methods are always swapping places, not really important but perhaps some kind of sorting on the methods could be applied?

Kind regards, Glen

@glever
Copy link
Author

glever commented Nov 5, 2017

Sorry, looking further at the javapoet sources the file write is in fact done in a try-with-resources block, so the final file should have contained all methods. Still, with my change it seems the getEntity() method is now always present...

@jpbelang
Copy link
Contributor

jpbelang commented Nov 5, 2017

Ths was fixed, rather accidentally in the current (2.1.1, current development).

@jpbelang
Copy link
Contributor

jpbelang commented Nov 5, 2017

Thank you very much for your help. As to the why it fails, it might be a windows file locking thing ? I'm as surprised as you are.

@glever
Copy link
Author

glever commented Nov 5, 2017

Probably, I'm not going to lose any sleep over it :)

btw: I find this project together with the raml-workbench great. This is how REST services should be developed in an enterprise setting!
Are there plans for creating jaxrs clients too? I'm interested in doing this myself but can't commit to anything as I already have other projects taking up my time...

@jpbelang
Copy link
Contributor

jpbelang commented Nov 5, 2017

Yes there are, but i'm running slower on this project for a bunch of reasons. There are a few too many bugs right now (which I'm trying to fix), and I'd like to separate type generation from jars generation (I have that half done).

I'll close the issue. Thank you very much for your support.

@jpbelang jpbelang self-assigned this Nov 5, 2017
@jpbelang jpbelang closed this as completed Nov 5, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants