swagger-api / swagger-core

Examples and server integrations for generating the Swagger API Specification, which enables easy access to your REST API
http://swagger.io
Apache License 2.0
7.37k stars 2.17k forks source link

ClassCastException in SpecReader #145

Closed jfhall closed 11 years ago

jfhall commented 11 years ago

Lines 90-99 of SpecReader.scala don't handle TypeVariableImpl appropriately, and the method attempts to cast a TypeVariableImpl to a class on line 91 causing a ClassCastException.

Using swagger4spring produces the following stack trace

java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:91) at com.wordnik.swagger.jsonschema.ApiModelParser.parsePropertyAnnotations(SwaggerJsonSchemaProvider.scala:150) at com.wordnik.swagger.jsonschema.ApiModelParser.com$wordnik$swagger$jsonschema$ApiModelParser$$parseMethod(SwaggerJsonSchemaProvider.scala:96) at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecurrsive$1.apply(SwaggerJsonSchemaProvider.scala:79) at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecurrsive$1.apply(SwaggerJsonSchemaProvider.scala:77) at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:34) at scala.collection.mutable.ArrayOps.foreach(ArrayOps.scala:38) at com.wordnik.swagger.jsonschema.ApiModelParser.parseRecurrsive(SwaggerJsonSchemaProvider.scala:77) at com.wordnik.swagger.jsonschema.ApiModelParser.parse(SwaggerJsonSchemaProvider.scala:68) at com.knappsack.swagger4springweb.parser.ApiParserImpl.createDocumentationSchemas(ApiParserImpl.java:154) at com.knappsack.swagger4springweb.parser.ApiParserImpl.processControllers(ApiParserImpl.java:76) at com.knappsack.swagger4springweb.parser.ApiParserImpl.createDocuments(ApiParserImpl.java:58) at com.knappsack.swagger4springweb.controller.ApiDocumentationController.getDocs(ApiDocumentationController.java:114) at com.knappsack.swagger4springweb.controller.ApiDocumentationController.getDocumentation(ApiDocumentationController.java:45)

fehguy commented 11 years ago

This should be fixed in 1.2.2-SNAPSHOT, can you try again?

fehguy commented 11 years ago

please re-open if 1.2.2-SNAPSHOT doesn't resolve this for you.

fehguy commented 11 years ago

Looks like it's still an issue. Per here:

https://groups.google.com/forum/#!topic/swagger-swaggersocket/eO2d6qD9K9g

This model:

public class DateTimeParam extends AbstractParam<DateTime> {
    public DateTimeParam(String param) {
        super(param);
    }
    protected DateTime parse(String param) {
        return DateConverter.convertToDateTime(param);
    }
    public Date getValueAsDate() {
        return getValue() == null ? null : getValue().toDate();
    }
    public static Date getValueAsDate(DateTimeParam dateTimeParam) {
        return dateTimeParam == null ? null : dateTimeParam.getValueAsDate();
    }
}

Used like this:

 public List<ResultFeedback> historical(@QueryParam("ownerId") Long ownerId,
                                           @ApiParam(value = "YYYY-MM-DD") @QueryParam("fromDate") DateTimeParam fromDate) {

public Response findEvents(
            @PathParam("ownerId") Long ownerId,
            @PathParam("registrationType") RegistrationTypeParam registrationType,
            @ApiParam(required = false) @QueryParam("status") AnimalExt.EnrollmentStatusCombination esc,
            @ApiParam(required = false) @QueryParam("unenrollmentDate") DateTimeParam unenrollmentDate,
            @ApiParam(required = false) @QueryParam("fromDate") DateTimeParam fromDate,
            @HeaderParam("If-Modified-Since") String modified) {

Is giving this stack trace:

1)

2013-03-18 13:55:41,489 ERROR com.wordnik.swagger.jaxrs.HelpApi : Unable to load model documentation for no.tine.api.jersey.DateTimeParam
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
    at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:91)
    at com.wordnik.swagger.jsonschema.ApiModelParser.parsePropertyAnnotations(SwaggerJsonSchemaProvider.scala:154)
    at com.wordnik.swagger.jsonschema.ApiModelParser.com$wordnik$swagger$jsonschema$ApiModelParser$$parseMethod(SwaggerJsonSchemaProvider.scala:98)
    at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecursive$1.apply(SwaggerJsonSchemaProvider.scala:81)
    at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecursive$1.apply(SwaggerJsonSchemaProvider.scala:79)

class def
public class DateTimeParam extends AbstractParam<DateTime> {

And 

2)

2013-03-18 13:55:41,500 ERROR com.wordnik.swagger.jaxrs.HelpApi : Unable to load model documentation for no.tine.api.jersey.RegistrationParam
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
    at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:84)
    at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(SpecReader.scala:70)
    at com.wordnik.swagger.jsonschema.ApiModelParser.parsePropertyAnnotations(SwaggerJsonSchemaProvider.scala:154)
    at com.wordnik.swagger.jsonschema.ApiModelParser.com$wordnik$swagger$jsonschema$ApiModelParser$$parseMethod(SwaggerJsonSchemaProvider.scala:98)
    at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecursive$1.apply(SwaggerJsonSchemaProvider.scala:81)
    at com.wordnik.swagger.jsonschema.ApiModelParser$$anonfun$parseRecursive$1.apply(SwaggerJsonSchemaProvider.scala:79)
engrun commented 11 years ago

Any news on this issue?

fehguy commented 11 years ago

I'm unable to reproduce this with 1.2.2-SNAPSHOT, if someone can help with a test case we can figure it out.

engrun commented 11 years ago

Ok, I have tried to reproduce and create a testcase be removing everything in my app except for the related classes. However, when doing this, I am no longer able to reproduce the error. So something else in my app must be influencing this. I'm suspecting frameworks that modify or somehow change the behaviour of my code - Spring aop proxies, spring security, ehcache proxies, jpa/cglib. For now I'm suppressing the errors in my log, as the doc/listing seems to work ok

etzlers commented 11 years ago

I am having the same class cast exception issue but in a different class.


15:27:32.212 [http-bio-8080-exec-3] DEBUG o.s.web.servlet.DispatcherServlet - Could not complete request
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
    at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(ApiPropertiesReader.scala:97) ~[swagger-core_2.9.1-1.2.2.jar:1.2.2]
    at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(ApiPropertiesReader.scala:83) ~[swagger-core_2.9.1-1.2.2.jar:1.2.2]

This is specifically referencing the line below in the ApiPropertiesReader class.

} else if (!returnType.getClass.isAssignableFrom(classOf[ParameterizedTypeImpl]) && returnType.asInstanceOf[Class[_]].isArray) {

This is an issue because generics are necessary for us, it would be great if this could be resolved quickly or if you could possibly offer some workaround as we are big fans of Swagger and would love to use it.

fehguy commented 11 years ago

If you can move to 1.2.3-SNAPSHOT the issues should be resolved. Can you please try?

etzlers commented 11 years ago

I still received the same exception

16:56:37.600 [http-bio-8080-exec-3] DEBUG o.s.web.servlet.DispatcherServlet - Could not complete request
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
    at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(ApiPropertiesReader.scala:97) ~[swagger-core_2.9.1-1.2.3-SNAPSHOT.jar:1.2.3-SNAPSHOT]
    at com.wordnik.swagger.core.ApiPropertiesReader$.getDataType(ApiPropertiesReader.scala:83) ~[swagger-core_2.9.1-1.2.3-SNAPSHOT.jar:1.2.3-SNAPSHOT]
fehguy commented 11 years ago

OK rats. Can you share what your model looks like?

etzlers commented 11 years ago

It's pretty basic but here's what it looks like

@JsonSerialize @JsonInclude(Include.NON_EMPTY) public class PageDTO< T > implements Serializable { private static final long serialVersionUID = 1L;

private List< T > content; private Long total;

public PageDTO() { content = new ArrayList< T >(); }

public PageDTO(T element) { content = new ArrayList< T >(); total = Long.valueOf(0); if(element != null) { content.add(element); total = Long.valueOf(1); } }

public PageDTO(List< T > content, Long total) { this.content = content; this.total = total; }

public List< T > getContent() { return content; }

public Long getTotal() { return total; }

public void setTotal(Long total) { this.total = total; }

public void setContent(List< T > content) { this.content = content; }

}

Sorry for the lack of code tags, I'm still new to the site and I couldn't get the < T > to display when I was using them.

fehguy commented 11 years ago

Thanks. Are you using Play or JAX-RS? And is this model a parameter in a POST method? Or the return value of a GET?

etzlers commented 11 years ago

We're actually using Spring MVC and this is part of a return value of a GET.

neowulf commented 11 years ago

+1

fehguy commented 11 years ago

I didn't write the Spring MVC integration but happy to help with it. If you can boil this down into a small sample project, I'll help sort it out.

wkennedy commented 11 years ago

Hey there, here is a WAR of a small sample project recreating the issue being seen with the DTO object above.

http://xosis.net/swagger/swagger4springweb-example-1.0.0-BUILD-SNAPSHOT.war

Here is the source code for the sample project:

http://xosis.net/swagger/swagger4spring-web-example.zip

Here is the source to the Spring MVC integration being referred to:

https://github.com/wkennedy/swagger4spring-web

I noticed this error happening when parsing the return type, but that seemed to be resolved in version 1.2.2. However, this issue appears to happen when I try parsing the model above using ApiModelParser. I updated to 1.2.3-SNAPSHOT and still noticed this issue happening. Here is the code from swagger4spring-web where the error occurs (ApiParserImpl.java line 162)

private void createDocumentationSchemas(Documentation documentation) {
    Reflections reflections = new Reflections(new ConfigurationBuilder()
            .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(baseModelPackage)))
            .setUrls(ClasspathHelper.forPackage(baseModelPackage))
            .setScanners(new SubTypesScanner(false), new ResourcesScanner()));
    Set<Class<? extends Object>> allModelClasses = reflections.getSubTypesOf(Object.class);
    for (Class<? extends Object> clazz : allModelClasses) {
        ApiModelParser parser;
        String schemaName;
        if (clazz.isArray()) {
            parser = new ApiModelParser(clazz.getComponentType());
            schemaName = clazz.getComponentType().getSimpleName();
        } else {
            parser = new ApiModelParser(clazz);
            schemaName = clazz.getSimpleName();
        }
        documentation.addModel(schemaName, parser.parse().toDocumentationSchema());
    }
}

Let me know if I can help, thanks!

fehguy commented 11 years ago

got it! Thanks for the sample. I've updated your pom to force swagger-core_2.9.1-1.2.3-SNAPSHOT.jar, added tomcat6 plugin (remove as you see fit) and access sonatype snapshots. I DID find an issue in ApiPropertiesReader as you mentioned, and added a simple fix. It's uploading to sonatype right now.

The pom is below, but there is one other issue you're going to hit. As is, the model schema for /api/v1/persons/ looks like this:

{
  "total": "long",
  "content": [
    "T"
  ]
}

Which I'm certain you don't really want content to be type T. This current has to be done by manually mapping the class to a concrete representation in your application bootstrap, if you want to have a proper concrete class. To do this, please take a look at this test class:

https://github.com/wordnik/swagger-core/blob/master/modules/swagger-core/src/test/scala/com/wordnik/test/swagger/core/SpecReaderTest.scala#L68

I'd update your sample but I'm out of time right now. Give it a whirl and post back. You're almost there!

Tony

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.knappsack</groupId>
  <artifactId>swagger4springweb-example</artifactId>
  <name>swagger4spring-web-example</name>
  <packaging>war</packaging>
  <version>1.0.0-BUILD-SNAPSHOT</version>
  <properties>
    <java-version>1.6</java-version>
    <org.springframework-version>3.1.4.RELEASE</org.springframework-version>
    <org.aspectj-version>1.6.10</org.aspectj-version>
    <org.slf4j-version>1.6.6</org.slf4j-version>
    <swagger-version>1.2.3-SNAPSHOT</swagger-version>
  </properties>

  <repositories>
    <repository>
      <id>sonatype snapshots</id>
      <name>sonatype snapshots</name>
      <layout>default</layout>
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
      <snapshots>
         <enabled>true</enabled>
      </snapshots>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
        <groupId>com.knappsack</groupId>
        <artifactId>swagger4spring-web</artifactId>
        <version>0.1.6</version>
        <exclusions>
            <exclusion>
                <artifactId>slf4j-log4j12</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
            <exclusion>
                <artifactId>junit</artifactId>
                <groupId>junit</groupId>
            </exclusion>
            <exclusion>
                <artifactId>slf4j-api</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- swagger -->
    <dependency>
      <groupId>com.wordnik</groupId>
      <artifactId>swagger-core_2.9.1</artifactId>
      <version>${swagger-version}</version>
      <scope>compile</scope>
    </dependency>

    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${org.springframework-version}</version>
      <exclusions>
        <!-- Exclude Commons Logging in favor of SLF4j -->
        <exclusion>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
         </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>

    <!-- AspectJ -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>${org.aspectj-version}</version>
    </dependency>  

    <!-- Logging -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${org.slf4j-version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>${org.slf4j-version}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${org.slf4j-version}</version>
      <scope>runtime</scope>
        <exclusions>
          <exclusion>
            <artifactId>log4j</artifactId>
            <groupId>log4j</groupId>
          </exclusion>
        </exclusions>
      </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.15</version>
      <exclusions>
        <exclusion>
          <groupId>javax.mail</groupId>
          <artifactId>mail</artifactId>
        </exclusion>
        <exclusion>
          <groupId>javax.jms</groupId>
          <artifactId>jms</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.sun.jdmk</groupId>
          <artifactId>jmxtools</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.sun.jmx</groupId>
          <artifactId>jmxri</artifactId>
        </exclusion>
      </exclusions>
      <scope>runtime</scope>
    </dependency>

    <!-- @Inject -->
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
    </dependency>

    <!-- Servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!-- Test -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.7</version>
      <scope>test</scope>
    </dependency>        
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <compilerArgument>-Xlint:all</compilerArgument>
          <showWarnings>true</showWarnings>
          <showDeprecation>true</showDeprecation>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat6-maven-plugin</artifactId>
        <version>2.1</version>
        <configuration>
          <port>8002</port>
          <path>/</path>
        </configuration>
        <executions>
          <execution>
            <id>run-tomcat</id>
            <phase>pre-integration-test</phase>
            <configuration>
              <fork>true</fork>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
        </plugins>
    </build>
</project>
wkennedy commented 11 years ago

Awesome, Tony! I've confirmed your fix does work. I'll update swagger4spring-web with version 1.2.3 as soon as it is released.

etzlers commented 11 years ago

Thank you so much!!

fehguy commented 11 years ago

released 1.2.3