Monday, November 15, 2010

troubleshooting procedure for Spring + Hessian

1) How to get HttpServletRequest object so as to call getRemoteAddr()


There is a request from client to restrict a limit of machines' IPs to access the web service developed by spring and hessian.
Definitely, There is no out-of-the-box function for Spring or Hessian to open HttpServletRequest to use.
Some smart guys have found some ways to resolve this problem.


Solution 1) modify the Hessian source code
refer to http://www.blogjava.net/Caixiaopig/archive/2007/08/03/134287.aspx


Solution 2) develop a customized HessianExporter
refer to http://wesee.javaeye.com/blog/663876


I prefer the second solution.


// HessianContext.java
package com.spring.hessian.test.exporter;

import javax.servlet.ServletRequest;

public class HessianContext {
    private ServletRequest _request;
    private static final ThreadLocal<HessianContext> _localContext = new ThreadLocal<HessianContext>() {

        public HessianContext initialValue() {
            return new HessianContext();
        }
    };

    private HessianContext() {
    }

    public static void setRequest(ServletRequest request) {
        _localContext.get()._request = request;
    }

    public static ServletRequest getRequest() {
        return _localContext.get()._request;
    }

    public static void clear() {
        _localContext.get()._request = null;
    }
}


// customized Hessian Exporter
package com.spring.hessian.test.exporter;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.remoting.caucho.HessianExporter;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.util.NestedServletException;

public class MyExporter extends HessianExporter implements HttpRequestHandler {

    @Override
    public void handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        if (!"POST".equals(request.getMethod())) {
            throw new HttpRequestMethodNotSupportedException(request
                    .getMethod(), new String[] { "POST" },
                    "HessianServiceExporter only supports POST requests");
        }

        response.setContentType(CONTENT_TYPE_HESSIAN);
        try {
           
            String clientIp = request.getRemoteAddr();
            System.out.println("----------->>> "+clientIp);
           
            if (!clientIp.equalsIgnoreCase("10.12.103.118")){
                throw new Exception("Invalid IP-----------------------");
            }
           
            HessianContext.setRequest(request);
            invoke(request.getInputStream(), response.getOutputStream());
        } catch (Throwable ex) {
             throw e;
        } finally {
            HessianContext.clear();
        }

    }
}
// your <servlet-name>-servlet.xml
 take note the class name in red, should put your customized hessian exporter class,not standard one
<bean name="/onepassService"
        class="com.spring.hessian.test.exporter.MyExporter">
        <property name="service" ref="accountInfoImpl" />
        <property name="serviceInterface" value="com.spring.hessian.test.IAccountInfo" />
 </bean>


I wonder how this guy worked it out !  :-(


2) can not get Spring Bean if using auto classpath scan

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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">

    <bean
        class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="order" value="1" />
    </bean>

    <bean
        class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
        <property name="defaultHandler" ref="httpRequestHandlerAdapter" />
        <property name="order" value="2" />
    </bean>

    <bean id="httpRequestHandlerAdapter"
        class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />

    <context:component-scan base-package="com.spring.hessian.test" />

</beans>

The three beans definition are required, otherwise, NullPointerException will occur due to system can not find the spring beans if adopting annotation to define spring bean.

3) setup SSL connection

3.1 generate keystore


keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 36000 -storepass changeit -alias myalias -keystore my.keystore -dname "CN=localhost,OU=mycomp,O=mycomp,L=SG,ST=SG,C=SG"


TAKE NOTE: set the value of CN carefully. if set CN=myhost, the URL of hessian servlet would be
https://myhost:port/xxxxxxxx


3.2 configure SSL connection in tomcat


edit %tomcat%/conf/server.xml and add following configuration


<Connector port="8843" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="C:/tmp/my.keystore" keystorePass="changeit" />


3.3 export cert for client side


keytool -exportcert -alias myalias -keystore my.keystore -file my.pem -rfc -storepass changeit


3.4 import cert to client's trust store


keytool -import -trustcacerts  -alias mycert_tomcat -file c:\tmp\my.pem  -keystore  cacerts


3.5 client caller source code


package com.spring.hessian.test;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import com.caucho.hessian.client.HessianProxyFactory;

public class HessianTester {

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Test
    public void test() throws Exception {
        System.setProperty("java.protocol.handler.pkgs", "javax.net.ssl");
        System.setProperty("javax.net.ssl.trustStore", "C:\\tmp\\cacerts");
        System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
     
         String url = "https://localhost:8843/springhessian/remote/myService";
        HessianProxyFactory factory = new HessianProxyFactory();
        IAccountInfo accountInfo = (IAccountInfo) factory.create(
                IAccountInfo.class, url);

        accountInfo.changeEmail(126, "def@email.com");
        accountInfo.deleteAccount(129);
    }
}


take note the property in red, previously, I set it as keyStore and always got the exception as follows.


Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
    at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
    at sun.security.validator.Validator.validate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    ... 38 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
    at java.security.cert.CertPathBuilder.build(Unknown Source)
    ... 44 more

No comments:

Post a Comment