Java

java programming

[Java] Cara Cache Data Hasil Query dengan SpringMVC

Pada tulisan kali ini, saya coba membuat suatu metode caching untuk menyimpan data-data master kedalam memory, sehingga tidak perlu query ke database lagi. Hal ini cocok digunakan untuk menyimpan data-data master yang sangat jarang berubah, seperti kode provinsi ataupun kode cabang.

Okay, di tutorial ini saya menggunakan Spring MVC 4, Hibernate dan ehCache sebagai cache provider. Berikut adalah pom.xml yang digunakan,

<?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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edw</groupId>
    <artifactId>mavenproject5</artifactId>
    <version>1</version>
    <packaging>war</packaging>

    <name>mavenproject5</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        
        
        <!-- Spring 4 dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.1.2.RELEASE</version>
         </dependency>
         
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        
        
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.2.2</version>
        </dependency>
        
        <!--ehcache-->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>
        
        <!--hibernate-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.3.8.Final</version>
        </dependency>
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Kemudian konfigurasi standard yang digunakan di SpringMVC, applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

    <context:annotation-config/>
    
    <tx:annotation-driven/>
    
    <cache:annotation-driven />
    
    <context:component-scan base-package="com.edw.mavenproject5.service"/>
    
     <!-- datasource  -->
    <bean id="ds" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"
          p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost/test"
          p:username="root" p:password=""
          p:initialSize="10"
          p:maxActive="50"
          p:maxIdle="10"
          p:minIdle="3"
          p:maxWait="30000"
          p:removeAbandoned="true"
          p:removeAbandonedTimeout="30"
          p:validationQuery="SELECT 1" />
    
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
        p:dataSource-ref="ds" p:configLocations="classpath:hibernate.cfg.xml" depends-on="ds">
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.MySQLDialect
                hibernate.query.substitutions=true
                hibernate.show_sql=true
                hibernate.enable_lazy_load_no_trans=true
            </value>
        </property>
    </bean> 
    
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
    <bean id="cacheManager" 
          class="org.springframework.cache.ehcache.EhCacheCacheManager" 
          p:cache-manager-ref="ehcache"/>
    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
          p:config-location="classpath:ehcache.xml"/>
    
</beans>

dan dispatcher-servlet.xml,

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">


    <context:component-scan base-package="com.edw.mavenproject5.controller" />
	
    <mvc:annotation-driven />
    <mvc:default-servlet-handler />

    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />
</beans>

yang kemudian dipanggil dari web.xml,

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

Nah, ada satu konfigurasi xml yang penting dalam membuat cache. Yaitu ehcache.xml, disana disimpan nama cache serta lama waktu hidupnya

<?xml version="1.0" encoding="UTF-8"?>
<!--
    caching configuration
-->
<ehcache>
    
    <defaultCache
        maxElementsInMemory="5000"
        eternal="true"
        overflowToDisk="false"
        memoryStoreEvictionPolicy="LRU"
    />
    
    <!--2menit-->
    <cache name="duaMenit"
           maxElementsInMemory="10000"
           timeToLiveSeconds="120"
           memoryStoreEvictionPolicy="LRU" 
    />
    
    <!--1menit-->
    <cache name="satuMenit"
           maxElementsInMemory="10000"
           timeToLiveSeconds="60"
           memoryStoreEvictionPolicy="LRU" 
    />
</ehcache>

Nah sekarang kita buat class java-nya, dimulai dari service layer. Perhatikan annotation Cacheable, konfigurasi tersebut adalah konfigurasi yang menandakan lokasi method yang kita cache.

package com.edw.mavenproject5.service;

import com.edw.mavenproject5.bean.Dosen;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class DosenService {

    @Autowired
    private SessionFactory sessionFactory;
    
    @Cacheable(key = "#root.methodName", value = "duaMenit")
    public List<Dosen> getDosens() {
        return sessionFactory.getCurrentSession().createCriteria(Dosen.class).list();
    }
    
    @Cacheable(key = "#root.methodName + #idDosen", value = "satuMenit")
    public Dosen getDosen(String idDosen) {
        return (Dosen) sessionFactory.getCurrentSession().get(Dosen.class, idDosen);
    }
    
}

Yang kemudian dipanggil dari controller,

package com.edw.mavenproject5.controller;

import com.edw.mavenproject5.service.DosenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class IndexController {
    
    @Autowired
    private DosenService dosenService;
    
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index(ModelMap modelMap){
        modelMap.put("dosens", dosenService.getDosens());
        return "index";
    }
    
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public String get(ModelMap modelMap, String id){
        modelMap.put("dosen", dosenService.getDosen(id));
        return "index";
    }
}

Dan berikut adalah ui yang digunakan untuk menampilkan hasil query didatabase,

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Hello World!</h1>
        
        <c:if test="${not empty dosens}">
        <table class="table table-hover table-striped">
            <thead>
                <tr>
                    <th>ID Dosen</th>
                    <th>Nama Dosen</th>
                </tr>
            </thead>
            <tbody>
            <c:forEach items="${dosens}" var="dosen">
                <tr>
                    <td>${dosen.iddosen}</td>
                    <td>${dosen.namadosen}</td>
                </tr>
            </c:forEach>
            </tbody>
        </table>
        </c:if>
        
        <c:if test="${not empty dosen}">
            <h1>Hello Dosen ${dosen.namadosen}!</h1>
        </c:if>
    </body>
</html>

Hasil akhirnya adalah sebagai berikut, data dengan kode 02 dan 321 sudah diubah secara langsung didatabase, namun walaupun browser di-refresh berulang kali, tetap menampilkan data yang lama, yaitu “Dodol” dan “Testing 123 123”.
capture1

Untuk sourcecode lengkapnya bisa diunduh di github, https://github.com/edwinkun/CachingWithSpringMVC. (*)

Error on Swagger 2 using Springfox – Request processing failed; nested exception is java.lang.IndexOutOfBoundsException: Index: 1

Had this very weird error, somehow it never happens before.

org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [dispatcher] in context with path [/test] threw exception [org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IndexOutOfBoundsException: Index: 1] with root cause
 java.lang.IndexOutOfBoundsException: Index: 1
	at java.util.Collections$EmptyList.get(Collections.java:3212)
	at springfox.documentation.swagger2.mappers.ModelMapper.typeOfValue(ModelMapper.java:129)
	at springfox.documentation.swagger2.mappers.ModelMapper.mapProperties(ModelMapper.java:92)
	at springfox.documentation.swagger2.mappers.ModelMapper.mapModels(ModelMapper.java:67)
	at springfox.documentation.swagger2.mappers.ModelMapper.modelsFromApiListings(ModelMapper.java:205)
	at springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl.mapDocumentation(ServiceModelToSwagger2MapperImpl.java:50)
	at springfox.documentation.swagger2.web.Swagger2Controller.getDocumentation(Swagger2Controller.java:82)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)

This is my pom.xml content,

		<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.5.0</version>
        </dependency>
 
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.5.0</version>
        </dependency>        

The solution is quite easy, upgrading into version 2.6.1 solve my problem.

Integrating Swagger 2 and Spring MVC 4

Most of tutorials on internet combines between Swagger and Spring Boot, i have to experiment for a while for a proper configuration for Spring MVC 4 and xml-based configuration because almost all my project are based on xml configuration instead of java configuration.

First is creating Java and Spring xml configuration, pom.xml

<?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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edw</groupId>
    <artifactId>Spring4Swagger</artifactId>
    <version>1</version>
    <packaging>war</packaging>

    <name>Spring4Swagger</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- Spring 4 dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency> 
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        
        <!-- json request -->        
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.1</version>
        </dependency>
        
        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.5.0</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.5.0</version>
        </dependency>
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

and web.xml,

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

next is applicationContext.xml,

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com.edw.spring4swagger.config" />
    <context:component-scan base-package="com.edw.spring4swagger.controller" />
</beans>

and last is dispatcher-servlet.xml, take a look at line 12 and 13

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- the mvc resources tag does the magic -->
    <mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/>
    <mvc:resources mapping="/swagger-ui.html" location="classpath:/META-INF/resources/swagger-ui.html"/>
    
    <mvc:annotation-driven />
</beans>

Next is java classes, first is my swagger config file,

package com.edw.spring4swagger.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@ComponentScan
@EnableSwagger2
@EnableWebMvc
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build().apiInfo(apiInfo());
    }
    
    private ApiInfo apiInfo() {
        ApiInfo apiInfo = new ApiInfo("Rest API", 
                                        "Rest API Example", 
                                        "1", 
                                        "", 
                                        new Contact("Edwin", "http://edwin.baculsoft.con", "edwin@baculsoft.com"), 
                                        "Apache License", 
                                        "");
        return apiInfo;
    }
}

Next is my java bean class,

package com.edw.spring4swagger.bean;

public class Test {

    private String name;
    private String address;
   
    public Test() {
    }
	
	// other setter getter
}

And last is, my controller class

package com.edw.spring4swagger.controller;

import com.edw.spring4swagger.bean.Test;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @ApiOperation(value = "Testing Page")
    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public Test test(@RequestParam String name, @RequestParam(required = false) String address) {
        Test test = new Test();
        test.setAddress(address);
        test.setName(name);
        return test;
    }
    
}

If everything runs well, you could go to /Spring4Swagger/swagger-ui.html to see your swagger page, this is what it looks like,

swaggerresult

swaggerresult2

You can browse to my github page, https://github.com/edwinkun/Spring4Swagger
Have fun (I)

Using Sitemesh and Got Error 330 (net::ERR_CONTENT_DECODING_FAILED) on Google Chrome

Basically i never had this error before, it become so challenging because it happen after i adding sitemesh library on my maven project. My first guess is, somehow Sitemesh have a conflicting configuration with other libraries. But i never found any reference nor article to support my theory. Even after i heavily removed several library, the error still happens.

Suddenly i have an enlightenment, after i remove my ehcache gzip compression filter. It turns out that in order to work i need to put sitemesh filter location after ehcache gzip filter.

Here is my final web.xml looks like,

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    
    <!-- gzip -->
    <filter>
        <filter-name>CompressionFilter</filter-name>
        <filter-class>net.sf.ehcache.constructs.web.filter.GzipFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CompressionFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- sitemesh -->
    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
</web-app>

SSLHandshakeException When Connecting to Let’s Encrypt CA SSL

Found this weird error when connecting to a HTTPS website, using apache commons httpclient.

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Very weird because i can connect to almost every other HTTPS website, but only one website causing this exception. After googling for a while, i found a very interesting solution from MKYong, that is to register the website’s certificate on my JVM.

http://www.mkyong.com/webservices/jax-ws/suncertpathbuilderexception-unable-to-find-valid-certification-path-to-requested-target/

Run this command to generate a new jssecacerts file,

c:\>java InstallCert localhost:443

The only difference is the location where i downloaded InstallCert.java, i downloaded it from this link.

https://github.com/OopsMouse/java-use-examples/blob/master/src/com/aw/ad/util/InstallCert.java

And i also removed the file’s package name, to make it easier to run.