phoenixnap / springmvc-raml-plugin

Spring MVC - RAML Spec Synchroniser Plugin. A Maven plugin designed to Generate Server & Client code in Spring from a RAML API descriptor and conversely, a RAML API document from the SpringMVC Server implementation.
Apache License 2.0
137 stars 84 forks source link

Missing import when using Spring4RestTemplateClientRule #61

Closed mehdijouan closed 8 years ago

mehdijouan commented 8 years ago

Hi, I created a spring java client based on a raml file which has a compilation issue:

    public ResponseEntity<List<com.test.samples.model.SampleUnit>> getSampleUnits() {
        HttpHeaders httpHeaders = new HttpHeaders();
        //  Add Accepts Headers and Body Content-Type
        ArrayList<MediaType> acceptsList = new ArrayList<MediaType>();
        acceptsList.add(MediaType.valueOf("application/json"));
        httpHeaders.setAccept(acceptsList);
        String url = baseUrl.concat("/sample_units");
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        UriComponents uriComponents = builder.build();
        HttpEntity httpEntity = new HttpEntity(httpHeaders);
        ParameterizedTypeReference<List<com.test.samples.model.SampleUnit>> typeReference = new ParameterizedTypeReference<List<SampleUnit>>() {

        }
        ;
        return this.restTemplate.exchange(uriComponents.encode().toUri(), HttpMethod.GET, httpEntity, typeReference);
    }

The auto generated method returns a type ResponseEntity<List<com.test.samples.model.SampleUnit>> which perfectly fine. The issue is at the line ...new ParameterizedTypeReference<List<SampleUnit>>()... where the returned object is referenced as SampleUnit and not com.test.samples.model.SampleUnit

As the class doesn't import the class com.test.samples.model.SampleUnit it leads to a compilation issue.

To expose that issue I updated my fork of the sample project https://github.com/mehdijouan/springmvc-raml-plugin-sample

kurtpa commented 8 years ago

Thanks for the sample project. I've seen something similar when we weren't adding classes to the same JCodeModel. I'll have to double check this one but should be an easy fix :)

kurtpa commented 8 years ago

" I'll have to double check this one but should be an easy fix :)" - famous last words. I cannot figure out why this is happening, i even rejiged all the model and controller generation to work on a common codemodel but this still did not fix this wierd behaviour. If anyone has any input on this one it will be most welcome :)

kurtpa commented 8 years ago

So far it seems that imports only work properly when classes are added to the classpath when the codemodel is being built. I'm going to try compile these model objects temporarily and then load them on the class path to see if this fixes the problem (for closure if anything else).

mehdijouan commented 8 years ago

I tried the following:

#%RAML 0.8

title: Samp API
version: v0.1
baseUri: sample_unit
mediaType:  application/json

schemas:
#  - sample_unit: !include schemas/sample_unit.json
  - sample_units: !include schemas/sample_units.json

/sample_units:
  displayName: Samples
  description: A collection of samples
#  post:
#    description: Creates a sample unit
#    body:
#      application/json:
#        schema: sample_unit
#        example: !include examples/example-sample_unit.json
#    responses:
#      201:
#        description: The sample unit has been successfully created.
  get:
    description: Retrieves samples
    responses:
      200:
        description: The sample unit collection has been successfully retrieved.
        body:
          application/json:
            schema: sample_units
            example: !include examples/example-sample_units.json
#  /{id}:
#    displayName: sample unit
#    description: A sample unit referenced by its identifier.
#    uriParameters:
#      id:
#        displayName: sample unit Identifier
#        description: The UUID of a sample unit
#        type: string
#    get:
#      description: Retrieves a given sample unit by its identifier.
#      responses:
#        200:
#          description: The sample unit has been successfully retrieved.
#          body:
#            application/json:
#              schema: sample_unit
#              example: !include examples/example-sample_unit.json
#        404:
#          description: The sample unit does not exist.

And the generated code worked as expected:

    /**
     * Retrieves samples
     * 
     */
    public ResponseEntity<List<SampleUnit>> getSampleUnits() {
        HttpHeaders httpHeaders = new HttpHeaders();
        //  Add Accepts Headers and Body Content-Type
        ArrayList<MediaType> acceptsList = new ArrayList<MediaType>();
        acceptsList.add(MediaType.valueOf("application/json"));
        httpHeaders.setAccept(acceptsList);
        String url = baseUrl.concat("/sample_units");
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        UriComponents uriComponents = builder.build();
        HttpEntity httpEntity = new HttpEntity(httpHeaders);
        ParameterizedTypeReference<List<SampleUnit>> typeReference = new ParameterizedTypeReference<List<SampleUnit>>() {

        }
        ;
        return this.restTemplate.exchange(uriComponents.encode().toUri(), HttpMethod.GET, httpEntity, typeReference);
    }

But, if both are set, it'll not work... It seems that there is a conflict between the two schemas sample_unit and sample_units.

mehdijouan commented 8 years ago

I run the plugin in debug to inspect why it does not works.

When taking a look at the logs I see that it builds twice JCodeModel:

...
com/test/samples/model/SampleUnit.java
com/test/samples/model/TArrayRef.java
com/test/samples/model/SampleUnit.java
com/test/samples/model/TArrayRef.java
...

But it is not the main issue.

When creating JCodeModel objects, you use the method CodeModelHelper.findFirstClassBySimpleName to find the right JClass to get. After debugging it, I noticed that for each simpleClassName named "SampleUnit", I get a different instance of JClass.

As the two JClass are seen as two different objects, The JCodeModel will not set an import and will use full class references (even if it's the same package and class name).

Having a shared JCodeModel between methods of the same generated client should solve the issue. What do you think ?

kurtpa commented 8 years ago

That's what i thought, i tried that but it didn't work. I even tried comoikung and adding them to the classloader but no happiness so far. I'll commit what i wrote to the feature branch soon

mehdijouan commented 8 years ago

Great news! Do you have plan to release it ;) ?

kurtpa commented 8 years ago

It's released with 0.8.4 :)