Integrating Swagger with Hippo CMS RESTful Resources

​ David Drake

​ 2015-08-24

swagger ui maven - integration Bloomreach CMS

The decoupling of content and presentation in Hippo CMS matches the present trend away from server-side HTML compilation to separate client and server codebases. The speed of development in this model depends heavily on the ease of integration with backend services. Frontend developers, whether internal or third-party, need up-to-date documentation and simple mechanisms for testing APIs.

Swagger has emerged as a de facto standard for documenting RESTful services. It makes API documentation part of the codebase rather than a separate (and thus often neglected) sibling.  In addition, it is easy to present and test using Swagger UI. In this post, we’ll show how simple it is to integrate Swagger into your Hippo CMS RESTful resources to make your content easy to find and consume.

Prerequisites

This lab assumes that you have a freshly built Hippo CMS project, as described in Hippo’s Getting Started documentation.  I built my app with com.dev9.hippo as my package base – you may need to modify these instructions if you use a different path.  If you run into problems with this lab, feel free to compare your code with the final state at https://github.com/dev9com/hippo-swagger-int.

🎯 Step 1: Setting up a Resource 

To get started, we’ll need some content to serve through our API, and we’ll need resources to serve it.  For the sake of simplicity, let’s use Hippo’s Essentials package to add both.

1. Build and start the application using

mvn clean verify
mvn -Pcargo.run -Drepo.path=storage.

2. Navigate to http://localhost:8080/essentials/#/library and install the “simple content” feature.

3. Navigate to http://localhost:8080/essentials/#/tools and select “Use Rest Services Setup”.

4. Set the endpoint name for the REST service to “rest”, and check “contentdocument” as the content type you wish to expose.

swagger ui maven Test plugin

5. Rebuild the project and run locally using once again the following: 

mvn clean verify
mvn -Pcargo.run -Drepo.path=storage.

For the rest of this lab, use this command when the instructions note that you need to rebuild.

At this point you should now have, under site/src/main/java/com/dev9/hippo/beans a new class called “ContentDocumention”, and under site/src/main/java/com/dev9/hippo/rest a new class called “ContentDocumentResource”.  A few new Spring configuration files have been added as well under site/src/main/resources/META-INF/hst-assembly/overrides, which you can examine to see how the new resource is wired into the Spring context.

Testing

To verify that this is wired correctly, you can try “curl http://localhost:8080/site/rest/ContentDocument” from the command line.  You should get back a couple of sample content documents.  If you’d like to test further, try adding new content documents using the CMS interface and repeating the command.

🎯 Step 2: Adding the Swagger Dependencies

Swagger has different maven dependencies depending on the framework being used to provide resources – Jersey 1, Jersey 2, Spring, etc.  For Hippo, which uses CXF, the correct dependency is io.swagger:swagger-jaxrs  Add the following to your site/pom.xml:

<!-- swagger dependency -->

<dependency>

  <groupId>io.swagger</groupId>

  <artifactId>swagger-jaxrs</artifactId>

  <version>1.5.0</version>

</dependency>

In addition, we’ll add two more dependencies – one for CORS support (as described in the hippo documentation: http://www.onehippo.org/library/concepts/rest/using-rest-api-with-ajax.html), and one to ensure xml processing is provided.

<!-- cors support -->

<dependency>

  <groupId>org.apache.cxf</groupId>

  <artifactId>cxf-rt-rs-security-cors</artifactId>

  <version>${cxf.version}</version>

</dependency>



<!-- work around for https://issues.onehippo.com/browse/ESSENTIALS-719 -->

<dependency>

  <groupId>com.fasterxml.jackson.jaxrs</groupId>

  <artifactId>jackson-jaxrs-xml-provider</artifactId>

  <version>2.4.5</version>

</dependency>


🎯 Step 3: Adding the Swagger Resource

Swagger documentation is served as a RESTful resource, returning json descriptions of the application’s API.  We’ll need to configure this resource alongside the application’s content resources, and for this we’ll use Spring.

While we’re setting up the Swagger resource we’ll override the default implementation to provide some initialization.  Swagger has only recently updated its API, and we’ll need some additional configuration to avoid a few bugs in the integration between Swagger’s json providers and swagger-ui’s model reader.

Here’s a sample resource that avoids these pitfalls.  We’ll exclude scanning of a packages outside of our sample application and specify that models should be interpreted using the JaxbAnnotationModel class.

public class HippoApiListingResource extends ApiListingResource {

    public HippoApiListingResource() {

        ModelConverters.getInstance().addPackageToSkip("org.hippoecm"); //skip hippo classes - only use the beans we create

        ModelConverters.getInstance().addPackageToSkip("org.onehippo"); //skip more hippo classes - only use the beans we create

        ModelConverters.getInstance().addPackageToSkip("javax"); //skip security classes

        Json.mapper().registerModule(new JaxbAnnotationModule()); //works around bug in Swagger 1.5.0 - they don't respect XmlAccessorType annotations

    }

}

The configuration file /src/main/resources/META-INF/hst-assembly/overrides/spring-plain-rest-api.xml currently contains the configuration for your ContentDocumentResource.   We’ll need to modify this by adding providers for the new content types, configuration for the swagger endpoint, and the swagger resource itself.   Documentation on this can be found at https://github.com/swagger-api/swagger-core/wiki/Java-CXF-Quickstart#specify-the-swagger-package, but you’ll need to modify it to fit in the Hippo CMS Spring framework.  In addition, we’ll wire in CORS support and our xml provider.  Replace the beans listed in the file with the following, below the existing imports in the file:

<!-- Swagger API listing resource -->

<bean id="apiListingResource" class="com.dev9.hippo.rest.HippoApiListingResource" />



<!-- cors support -->

<bean id="jaxrsRestCorsFilter" class="org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter"/>



<!-- xml provider -->

<bean id="jacksonXmlProvider" class="com.fasterxml.jackson.jaxrs.xml.JacksonJaxbXMLProvider"/>



<!-- Swagger writers -->

<bean id="swaggerSerializers" class="io.swagger.jaxrs.listing.SwaggerSerializers" />



<bean id="customRestPlainResourceProviders" class="org.springframework.beans.factory.config.ListFactoryBean">

  <property name="sourceList">

    <list>

      <bean class="org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider">

        <constructor-arg>

          <bean class="com.dev9.hippo.rest.ContentDocumentResource"/>

        </constructor-arg>

      </bean>

      <bean class="org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider">

        <constructor-arg>

          <ref bean="apiListingResource" />

        </constructor-arg>

      </bean>

    </list>

  </property>

</bean>



<!-- Custom Entity Providers which are annotated with javax.ws.rs.ext.Provider. -->

<bean id="customJaxrsRestEntityProviders" class="org.springframework.beans.factory.config.ListFactoryBean">

  <property name="sourceList">

    <list>

      <ref bean="swaggerSerializers" />



      <ref bean="jaxrsRestCorsFilter"/>

      <ref bean="jacksonXmlProvider"/>

    </list>

  </property>

</bean>



<!-- this scans the classes for resources -->

<bean id="swaggerConfig" class="io.swagger.jaxrs.config.BeanConfig">

  <property name="resourcePackage" value="com.dev9.hippo.rest"/>

  <property name="version" value="1.0.0"/>

  <property name="basePath" value="site/rest"/>

  <property name="title" value="Hippo Swagger Integration Demo"/>

  <property name="description" value="Demonstrates how to Integrate Swagger with Hippo CMS"/>

  <property name="contact" value="[email protected]"/>

  <property name="license" value="Apache 2.0 License"/>

  <property name="licenseUrl" value="http://www.apache.org/licenses/LICENSE-2.0.html"/>

  <property name="scan" value="true"/>

</bean>

Below remark about commenting out some part is only needed in version older than CMS 12.0

In addition, we’ll need to comment out imports of SpringComponentManager-rest-plain-pipeline.xml in any other spring configuration files, to ensure that it is overridden correctly – in other words, it should only be included in spring-plain-rest-api.xml.  Comment out the following lines from both template-composer-rest.xml and hst-cms-rest.xml in site/src/main/resources/META-INF/hst-assembly/overrides/:

<!--<import resource="classpath:/org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-jackson.xml" />-->


Testing

With this complete, you should have all configuration complete.  Rebuild and run the application to verify that the configuration is correct.

You can now navigate to http://localhost:8080/site/rest/api-docs in the browser to look at the documentation, but since you don’t have any documented resources at this point, you should see nothing returned.

🎯 Step 4: Adding API Documentation Annotations to Resources

For Swagger to detect the resource during classpath scanning, you’ll need to add documentation annotations to your resources.  The Swagger resource will not add list resources missing these annotations.

Swagger annotations only support a handful of common generics, so we’ll need to create a concrete class to assist it in interpreting our response models.  The ContentDocumentResource returns a Pageable<ContentDocument>, so the class shown below will be used for documentation.

public class PageableContentDocument extends DefaultPagination<ContentDocument> {

    public PageableContentDocument(List<ContentDocument> items) {

        super(items);

    }

}

Swagger annotations are described here: https://github.com/swagger-api/swagger-core/wiki/Annotations.  At minimum, you’ll need to add the @Api annotation to the resource class and @ApiOperation to its methods.  The finished product should look something like this:

@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})

@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.APPLICATION_FORM_URLENCODED})

@Path("/ContentDocument/")

@Api(value = "/ContentDocument", description = "ContentDocument REST Operations")

public class ContentDocumentResource extends BaseRestResource {



    @GET

    @Path("/")

    @ApiOperation(value = "Get all ContentDocument beans.", response = PageableContentDocument.class)

    public Pageable<ContentDocument> index(@Context HttpServletRequest request) {

        return findBeans(new DefaultRestContext(this, request), ContentDocument.class);

    }



    //and so on…

}


Testing

After a rebuild, you should now be able to navigate to http://localhost:8080/site/rest/api-docs and see basic site documentation, including a list of all endpoints.

As resources are added and documented, they will be automatically added to this endpoint, making your API easily discoverable.   At this point one could add additional annotations to the beans, documenting their usage – see the github example for details.

🎯 Step 5: Using Swagger UI

The Swagger resource is concise and informative, but Swagger UI makes the documentation human-readable and executable.  It can be found here: https://github.com/swagger-api/swagger-ui, and is essentially a static website that can communicate with the swagger json to provide an interface. 

One way to use it would be to include it in your site (and my example site shows one way to use this, by adding it to your bootstrapped webfiles).  If you do so, you don’t actually need to enable CORS support, since it is served from the same host.

But the Swagger UI need not be served from the same host, since CORS is now enabled.  You can navigate to the sample Swagger UI page located at http://petstore.swagger.io/ and insert the url for your site’s documentation.  Hit explore, and you should see a page that looks like this:

swagger ui maven - swagger Integration

 

By selecting the individual endpoints, you can send requests to the site you are currently hosting and see real responses.  In addition, you can explore the models that will be returned and see documentation of the parameters required by endpoints.

swagger ui maven - response class

📚 Conclusion

This lab demonstrated how any Hippo site with RESTful resources can be simply documented with Swagger’s in-line annotations to provide an easily discoverable API.  When combined with Swagger UI, it’s an elegant replacement for external documentation.  

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?