Java keytool

1. import (intermediate) CA cert into Java keystore

keytool -import -trustcacerts -alias quovadis-global-ssl-ica-g3 -file QuoVadis-ICA-G3.cert -keystore cacerts

2. list certs in Java keystore

keytool -list -v -keystore cacerts

Aixs2: disable chunked encoding

It seems that iCheque doesn’t support chunked transfer in their new version of payment API, but chunked transfer is enabled in AXIS2 by default, so you will get

org.apache.axis2.AxisFault: Transport error: 411 Error: Length Required

When chunked transfer is enabled, the Content-Length will not be present in the HTTP header of request. Because by using chunked transfer, the sender can dynamically generate the content, and send it, the sender doesn’t need to know the length of the content. Another HTTP header, Transfer-Encoding: chunked, will be put in.

If the receiver doesn’t support chunked transfer, you have to disable it.

In AXIS2, you can disable it like this

serviceStub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE)

Compile, install Apache Portable Runtime (APR) on Ubuntu

Compile and install APR
root needed!

Download source packages, apr-1.5.2.tar.gz and apr-util-1.5.4.tar.gz, from Apache site, https://apr.apache.org/download.cgi

Unpack the packages to /root/apache/apr-1.5.2 and /root/apache/apr-util-1.5.4

Installation path: /usr/local/apr

# compile and install apr
# /root/apache/apr-1.5.2
./configure --prefix=/usr/local/apr
make
make install
# compile and install apr-util
#/root/apache/apr-util-1.5.4
./configure --prefix=/usr/local/apr/lib --with-apr=/usr/local/apr
make
make install

For Tomcat
Compile and install tomcat native
Go to tomcat/bin, unpack tomcat-native.tar.gz, then go to tomcat-native-1.1.29-src/jni/native, compile and install tomcat-native

# /home/root/apache-tomcat-7.0.53/bin/tomcat-native-1.1.29-src/jni/native
./configure --with-apr=/usr/local/apr --with-java-home=/usr/lib/jvm/java-VERSION-oracle/
make
make install

Configuration
In the start script of Tomcat, add

CATALINA_OPTS="$CATALINA_OPTS -Djava.library.path=/usr/local/apr/lib"

In conf/server.xml, update protocol of connectors to use the following protocols,

org.apache.coyote.http11.Http11AprProtocol
org.apache.coyote.ajp.AjpAprProtocol

Restart tomcat, if you see the following in the catalina.out and no exception, the APR is running.

...
INFO: Loaded APR based Apache Tomcat Native library 1.1.29 using APR version 1.5.2.
Nov 26, 2015 2:58:54 PM org.apache.catalina.core.AprLifecycleListener init
...

BigDecimal.divide: Non-terminating decimal expansion

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

在使用BigDecimal进行除法运算时,在除不尽的情况下,Java会抛出上述异常。
解决办法是在调用divide方法的另一个实现,指定精度及舍入模式

BigDecimal.TEN.divide(new BigDecimal('3'), 2, RoundingMode.HALF_UP)

Ubuntu 中升级至 JDK7

sudo apt-get update
sudo apt-get install software-properties-common python-software-properties
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java7-installer
sudo update-alternatives --config java

HttpClient: SSL双向认证

拿到PPro支付的文档后,发现PPro需要客户端在与其交互的过程中,使用SSL双向认证。

也就是说,申请测试账户后,PPro会为测试账户生成一个ssl客户端证书,该证书是由PPro自签名CA签发的。

在PPro分配测试账号时,用户会得到

  • ca.crt – PPro自签名CA根证书
  • customer.crt – 用户证书
  • customer.key – 用户私钥

为了在Java中使用这些证书及私钥,需要将其导入到KeyStore中,需要创建两个KeyStore,分别存放CA证书,用户证书及私钥

1. 导入ca.crt至caKeyStore

keytool -importcert -alias pproca -file ca.crt -storetype jks -keystore caKeyStore.jks

需要指定别名、KeyStore类型(JKS),并指定KeyStore的访问密码

2. 导入customer.crt和customer至clientKeyStore

openssl pkcs12 -export -in customer.crt -out clientKeyStore.p12 -inkey customer.key -name customer

导入客户证书及密钥不能使用keytool命令,而需要使用openssl中的pkcs12 -export,并指定证书、密钥,以及别名(-name),并且需要根据提示输入export密码。keytool不能导入密钥。

openssl命令在此处生成的clientKeyStore.p12可以直接在Java中当作类型为PKCS12的KeyStore来使用。

加载KeyStore

InputStream keyStoreStream = loadKeyStoreIntoInputStream(keyStoreLocation);
KeyStore keyStore = KeyStore.getInstance("pkcs12"); // jks for CA
keyStore.load(keyStoreStream, keyStorePassword.toCharArray());

HttpClient调用KeyStore实现双向SSL认证

        HttpClient httpClient = new DefaultHttpClient();
 
        SSLSocketFactory sslSocketFactory = new SSLSocketFactory(clientKeyStore, clientKeyStorePassword, caKeyStore);
        Scheme protocolScheme = new Scheme("HTTPS", 443, sslSocketFactory);
 
        httpClient.getConnectionManager().getSchemeRegistry().register(protocolScheme);
 
        HttpPost httpPost = new HttpPost(url);

代码中的clientKeyStorePassword即是生成p12库时指定的export密码。

参考:OpenSSL设置、配置 及相关命令, KeyStore in Java

在Spring中使用Cache缓存方法返回

刚好在项目中用到了Spring中的Cache缓存服务层方法的返回值

使用相当简单,直接用@Cacheable注解标注需要缓存返回值的方法,如下

@Cacheable(value = "billfoldCache", key = "'fetchByGroupName' + #groupName")
Collection<Game> fetchByGroupName(final String groupName) {
}

其中value的值是缓存的名字,在ehcache配置文件中指定(如果使用ehcache作为底层缓存),key是缓存的键,并支持Spring Expression Language (SpEL)。

配置
目前Spring中带了两种实现,一是基于ehcache,另一个是基于Java中的ConcurrentMap实现。项目是使用的是ehcache

配置也很简单,在Spring配置文件中加入

    <cache :annotation-driven cache-manager="billfoldCacheManager"></cache>
 
    <bean id="billfoldCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"
          p:cacheManager-ref="billfoldEhCacheManagerFactoryBean"></bean>
 
    <bean id="billfoldEhCacheManagerFactoryBean"
          class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
          p:configLocation="classpath:/ctx-ehcache.xml"></bean>

并且为ehcache创建名为ctx-ehcache.xml的配置文件

< ?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false">
    <diskstore path="java.io.tmpdir"></diskstore>
    <defaultcache maxEntriesLocalHeap="100000" eternal="false"
                  timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="1000000"
                  diskExpiryThreadIntervalSeconds="3600" memoryStoreEvictionPolicy="LFU">
    </defaultcache>
 
    <cache name="billfoldCache" eternal="false" maxEntriesLocalHeap="100000"
           timeToLiveSeconds="60" timeToIdleSeconds="60">
        <persistence strategy="none"></persistence>
    </cache>
</ehcache>

需要注意的是:

  • Spring推荐在实现类上使用@Cache*注解,而不是在接口上使用
  • 在默认的配置下(proxy),@Cache*只对外部调用具有public可见性的方法起作用,protected, private, package-visible以及内部方法调用,缓存都不会生效。除非使用在配置时明确指定mode为aspectj。详细见Spring CacheSpring Configuration – load time weaver

在JBoss Seam中使用GWT

这几天在JBoss Seam集成GWT,试了很多时间,总结出一些问题:

JBoss Seam版本:2.1.0.SP1

Google Web Toolkit版本:1.5.3

  1. gwt-servlet.jar版本问题:Seam中自带的gwt-servlet.jar版本可能和你使用的GWT版本不一致,如果你编译GWT程序的版本与Seam中gwt-servlet.jar版本不一致,可能会出现一些奇怪的问题,最好的办法是将Seam中的gwt-servlet.jar换成GWT中带的jar。
  2. Seam Resource Servlet的配置和GWT程序中Service Entry Point的设置:Seam中的默认url pattern是/seam/resource/*,所以Seam会把对/seam/resource/gwt/*的请求发送给org.jboss.seam.remoting.gwt.GWT14Service处理,GWTService才会根据GWT客户端发送的请求类和方法来进行调用。需要注意的是,根据跟踪Seam的GWTService发现,客户端发送的请求类似于“5|0|6|http://127.0.0.1:8080/SeamGWT/|5BA8A5B3E35F40698BB0BF65F390BCF2|com.tiandinet.gwt.hello.client.HelloService|sayHello|java.lang.String|your name|1|2|3|4|1|5|6|”,而Seam的GWTService.getResource会根据com.tiandinet.gwt.hello.client.HelloService名称查找Seam组件,此组件即为GWT中的远程服务接口的Seam实现,所以在设置此实现类的@Name属性时,需要将其设置为GWT中远程服务接口的类名。
    所以,对于Service Entry Point的设置,只要URL能匹配到/seam/resource/gwt/即可,而Seam Reference示例中的String endpointURL = GWT.getModuleBaseURL() + “seam/resource/gwt”;可能不一定正确,因为根据GWT编译后页面路径在Seam应用中所处的位置不同,GWT.getModuleBaseURL()返回的路径可能就不能匹配到/seam/resource/gwt。
  3. Seam Resource Servlet映射
    /web=org.jboss.seam.ui.resource.WebResource
    /captcha=org.jboss.seam.captcha.CaptchaImage
    /remoting=org.jboss.seam.remoting.Remoting
    /gwt=org.jboss.seam.remoting.gwt.GWT14Service
    /graphicImage=org.jboss.seam.ui.graphicImage.GraphicImageResource