关于hessian中使用Session的问题

 

  本帖旨在讨论hessian的session以及cookie问题,对于hessian的使用网上和官方已经有很多例子,故不在此做讨论。
  最近在做一个项目,client面临多种平台的手机客户端以及pc浏览器的flash,在寻找rpc方案的过程中,最初选择使用phprpc,而phprpc没有Objective-c的client版本(Hprose虽有此版,但似乎收费),后转道hessian,hessian使用过程中发现,hessian的client不支持cookie机制,进而导致了其server端不支持共享session,也就是说,由于hessian使用的是http协议且client的实现不支持cookie,从而导致了client对server的每次调用都会建立一个新的session,而如果在每次调用都加入固定的类似jsessionid或userid这种参数的话,似乎看着很不爽,看到网上有篇帖子http://qieqie.iteye.com/blog/82492,看到有人使用aop在http协议层对于使用者透明的增加和剥离这个参数,本人还是觉得不爽,因为我的服务端架构中,已经确定了一个集群方案,而其中对于集群的session准备使用memcached-session-manager,从而要求我在改架构中需要使用标准的http session进行存储。
无奈之下,决定扩展hessian(我使用的是4.0.7),期间曾看到网上高手修改过较早版本的hessian(见贴http://hi.baidu.com/li_zhongnan/blog/item/37fe8ffaefe9ad17a9d31153.html),而这种方式我尝试后发现仍无法使用cookie,后发现原来新版的HessianProxy中的parseResponseHeaders方法已经被废弃使用,于是翻看源码,最后发现在HessianURLConnection(这个类可能在较早的hessian中没有)中有有了一个新的parseResponseHeaders方法,于是扩展开始,为了不修改源码,我自己新建了一个工程,并作为基础工程被我的client所引用,这里我只贴出核心部分的class代码,有兴趣的朋友可以在此基础上进一步完善:
Java代码 复制代码  收藏代码
  1. package com.caucho.hessian.client;   
  2.   
  3. import java.io.IOException;   
  4. import java.net.HttpURLConnection;   
  5. import java.net.URL;   
  6. import java.net.URLConnection;   
  7. import java.util.List;   
  8.   
  9. /**  
  10.  *   
  11.  *   
  12.  * @author liliang  
  13.  * @descirption 为维持session而扩展的支持cookie的连接  
  14.  */  
  15. public class CookieHessianURLConnection extends HessianURLConnection {   
  16.   
  17.     /**  
  18.      * @description 共享cookie  
  19.      */  
  20.     private static List<String> cookies = null;   
  21.   
  22.     CookieHessianURLConnection(URL url, URLConnection conn) {   
  23.         super(url, conn);   
  24.     }   
  25.        
  26.     @Override  
  27.     protected void parseResponseHeaders(HttpURLConnection conn)   
  28.             throws IOException {   
  29.         super.parseResponseHeaders(conn);   
  30.         List<String> _cookies = conn.getHeaderFields().get("Set-Cookie");   
  31.         if (_cookies != null)   
  32.             cookies = _cookies;   
  33.     }   
  34.   
  35.     public void addRequestHeaders() {   
  36.         if (cookies != null) {   
  37.             for (String cookieString : cookies) {   
  38.                 addHeader("Cookie", cookieString);   
  39.             }   
  40.         }   
  41.     }   
  42.   
  43. }  
package com.caucho.hessian.client;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

/**
 * 
 * 
 * @author liliang
 * @descirption 为维持session而扩展的支持cookie的连接
 */
public class CookieHessianURLConnection extends HessianURLConnection {

	/**
	 * @description 共享cookie
	 */
	private static List<String> cookies = null;

	CookieHessianURLConnection(URL url, URLConnection conn) {
		super(url, conn);
	}
	
	@Override
	protected void parseResponseHeaders(HttpURLConnection conn)
			throws IOException {
		super.parseResponseHeaders(conn);
		List<String> _cookies = conn.getHeaderFields().get("Set-Cookie");
		if (_cookies != null)
			cookies = _cookies;
	}

	public void addRequestHeaders() {
		if (cookies != null) {
			for (String cookieString : cookies) {
				addHeader("Cookie", cookieString);
			}
		}
	}

}




Java代码 复制代码  收藏代码
  1. package com.caucho.hessian.client;   
  2.   
  3. import java.io.IOException;   
  4. import java.net.URL;   
  5. import java.net.URLConnection;   
  6. import java.util.logging.Level;   
  7. import java.util.logging.Logger;   
  8.   
  9. /**  
  10.  *   
  11.  *   
  12.  * @author liliang  
  13.  * @descirption 连接工厂类 主要使用扩展后的CookieHessianURLConnection来维持连接  
  14.  *              open方法除返回值外完全摘自源码HessianURLConnectionFactory  
  15.  */  
  16. public class CookieHessianURLConnectionFactory extends  
  17.         AbstractHessianConnectionFactory {   
  18.   
  19.     private static final Logger log = Logger   
  20.             .getLogger(CookieHessianURLConnectionFactory.class.getName());   
  21.   
  22.     @Override  
  23.     public HessianConnection open(URL url) throws IOException {   
  24.   
  25.         if (log.isLoggable(Level.FINER))   
  26.             log.finer(this + " open(" + url + ")");   
  27.   
  28.         URLConnection conn = url.openConnection();   
  29.   
  30.         // HttpURLConnection httpConn = (HttpURLConnection) conn;   
  31.         // httpConn.setRequestMethod("POST");   
  32.         // conn.setDoInput(true);   
  33.   
  34.         long connectTimeout = getHessianProxyFactory().getConnectTimeout();   
  35.   
  36.         if (connectTimeout >= 0)   
  37.             conn.setConnectTimeout((int) connectTimeout);   
  38.   
  39.         conn.setDoOutput(true);   
  40.   
  41.         long readTimeout = getHessianProxyFactory().getReadTimeout();   
  42.   
  43.         if (readTimeout > 0) {   
  44.             try {   
  45.                 conn.setReadTimeout((int) readTimeout);   
  46.             } catch (Throwable e) {   
  47.             }   
  48.         }   
  49.   
  50.         /*  
  51.          * // Used chunked mode when available, i.e. JDK 1.5. if  
  52.          * (_proxyFactory.isChunkedPost() && conn instanceof HttpURLConnection)  
  53.          * { try { HttpURLConnection httpConn = (HttpURLConnection) conn;  
  54.          *   
  55.          * httpConn.setChunkedStreamingMode(8 * 1024); } catch (Throwable e) { }  
  56.          * }  
  57.          */  
  58.   
  59.         return new CookieHessianURLConnection(url, conn);   
  60.   
  61.     }   
  62.   
  63. }  
package com.caucho.hessian.client;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 
 * 
 * @author liliang
 * @descirption 连接工厂类 主要使用扩展后的CookieHessianURLConnection来维持连接
 *              open方法除返回值外完全摘自源码HessianURLConnectionFactory
 */
public class CookieHessianURLConnectionFactory extends
		AbstractHessianConnectionFactory {

	private static final Logger log = Logger
			.getLogger(CookieHessianURLConnectionFactory.class.getName());

	@Override
	public HessianConnection open(URL url) throws IOException {

		if (log.isLoggable(Level.FINER))
			log.finer(this + " open(" + url + ")");

		URLConnection conn = url.openConnection();

		// HttpURLConnection httpConn = (HttpURLConnection) conn;
		// httpConn.setRequestMethod("POST");
		// conn.setDoInput(true);

		long connectTimeout = getHessianProxyFactory().getConnectTimeout();

		if (connectTimeout >= 0)
			conn.setConnectTimeout((int) connectTimeout);

		conn.setDoOutput(true);

		long readTimeout = getHessianProxyFactory().getReadTimeout();

		if (readTimeout > 0) {
			try {
				conn.setReadTimeout((int) readTimeout);
			} catch (Throwable e) {
			}
		}

		/*
		 * // Used chunked mode when available, i.e. JDK 1.5. if
		 * (_proxyFactory.isChunkedPost() && conn instanceof HttpURLConnection)
		 * { try { HttpURLConnection httpConn = (HttpURLConnection) conn;
		 * 
		 * httpConn.setChunkedStreamingMode(8 * 1024); } catch (Throwable e) { }
		 * }
		 */

		return new CookieHessianURLConnection(url, conn);

	}

}



Java代码 复制代码  收藏代码
  1. public class MyHessianProxy extends HessianProxy {   
  2.   
  3.     public MyHessianProxy(URL url, HessianProxyFactory factory, Class<?> api) {   
  4.         super(url, factory, api);   
  5.     }   
  6.   
  7.     /** Add cookie list to request headers */  
  8.     @Override  
  9.     protected void addRequestHeaders(HessianConnection conn) {   
  10.         super.addRequestHeaders(conn);   
  11.         if(conn instanceof CookieHessianURLConnection) {   
  12.             CookieHessianURLConnection connection = (CookieHessianURLConnection)conn;   
  13.             connection.addRequestHeaders();   
  14.         }   
  15.            
  16.     }   
  17.   
  18. }  
public class MyHessianProxy extends HessianProxy {

	public MyHessianProxy(URL url, HessianProxyFactory factory, Class<?> api) {
		super(url, factory, api);
	}

	/** Add cookie list to request headers */
	@Override
	protected void addRequestHeaders(HessianConnection conn) {
		super.addRequestHeaders(conn);
		if(conn instanceof CookieHessianURLConnection) {
			CookieHessianURLConnection connection = (CookieHessianURLConnection)conn;
			connection.addRequestHeaders();
		}
		
	}

}



Java代码 复制代码  收藏代码
  1. package com.mostar.common.hessian;   
  2.   
  3. import java.lang.reflect.InvocationHandler;   
  4. import java.lang.reflect.Proxy;   
  5. import java.net.URL;   
  6.   
  7. import com.caucho.hessian.client.HessianProxyFactory;   
  8. import com.caucho.hessian.io.HessianRemoteObject;   
  9.   
  10. public class MyHessianProxyFactory extends HessianProxyFactory {   
  11.     @Override  
  12.     public Object create(Class<?> api, URL url, ClassLoader loader) {   
  13.         if (api == null)   
  14.             throw new NullPointerException(   
  15.                     "api must not be null for HessianProxyFactory.create()");   
  16.         InvocationHandler handler = null;   
  17.   
  18.         handler = new MyHessianProxy(url, this, api);   
  19.   
  20.         return Proxy.newProxyInstance(loader, new Class[] { api,   
  21.                 HessianRemoteObject.class }, handler);   
  22.     }   
  23. }  
package com.mostar.common.hessian;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URL;

import com.caucho.hessian.client.HessianProxyFactory;
import com.caucho.hessian.io.HessianRemoteObject;

public class MyHessianProxyFactory extends HessianProxyFactory {
	@Override
	public Object create(Class<?> api, URL url, ClassLoader loader) {
		if (api == null)
			throw new NullPointerException(
					"api must not be null for HessianProxyFactory.create()");
		InvocationHandler handler = null;

		handler = new MyHessianProxy(url, this, api);

		return Proxy.newProxyInstance(loader, new Class[] { api,
				HessianRemoteObject.class }, handler);
	}
}



  代码中的cookies 之所以使用了static,主要是考虑一个client公用一个session,而非早起的IE浏览器每个进程新开一个session的机制。

客户端调用例子:
Java代码 复制代码  收藏代码
  1. // 设置连接工厂类   
  2. System.setProperty(HessianConnectionFactory.class.getName(),   
  3.         CookieHessianURLConnectionFactory.class.getName());   
  4. String url = "http://10.0.8.1:8080/test/hello.service";   
  5. MyHessianProxyFactory factory = new MyHessianProxyFactory();   
  6. IHellowordService hellowordService = (IHellowordService) factory   
  7.         .create(IHellowordService.class, url);   
  8. System.out.println(hellowordService.sayHello("myname"));  
		// 设置连接工厂类
		System.setProperty(HessianConnectionFactory.class.getName(),
				CookieHessianURLConnectionFactory.class.getName());
		String url = "http://10.0.8.1:8080/test/hello.service";
		MyHessianProxyFactory factory = new MyHessianProxyFactory();
		IHellowordService hellowordService = (IHellowordService) factory
				.create(IHellowordService.class, url);
		System.out.println(hellowordService.sayHello("myname"));


修改后经测试,在服务器端可以使用如下代码获取request以及session了:
Java代码 复制代码  收藏代码
  1. HttpServletRequest req = (HttpServletRequest) ServiceContext   
  2.         .getContextRequest();   
  3. req.getSession();  
		HttpServletRequest req = (HttpServletRequest) ServiceContext
				.getContextRequest();
		req.getSession();


但问题又来了,我在测试的时候发现,这种获取request及session的方法只有在使用hessian自带的HessianServlet进行服务发布才可用,而如果使用spring mvc和spring的HessianServiceExporter进行hessian的服务发布的话,那么这种获取request和session的方式仍不可用,查看spring3的源码惊奇的发现,在HessianServiceExporter以及HessianExporter的实现中,并没有像hessian的servlet那样调用ServiceContext.begin和ServiceContext.end()方法,难道是spring仔细查看hessian的源码或api,还是说spring不想让开发者拿到servlet容器相关的request和session之类的实例,但不管怎么样,我有这样的需求,于是又扩展了spring的HessianServiceExporter(3之前的版本实现可能稍有区别):
Java代码 复制代码  收藏代码
  1. /**  
  2.  *   
  3.  *  
  4.  * @author liliang  
  5.  * @descirption 对spring的HessianServiceExporter进行扩展,以能够在逻辑service中使用httpRequest  
  6.  */  
  7. public class HessianServiceContextExporter extends HessianServiceExporter {   
  8.        
  9.     @Override  
  10.     public void handleRequest(HttpServletRequest request,   
  11.             HttpServletResponse response) throws ServletException, IOException {   
  12.   
  13.         try {   
  14.             //   
  15.             String serviceId = request.getPathInfo();   
  16.             String objectId = request.getParameter("id");   
  17.             if (objectId == null)   
  18.                 objectId = request.getParameter("ejbid");   
  19.   
  20.             ServiceContext.begin(request, serviceId, objectId);   
  21.   
  22.             // 调用spring自身的实现   
  23.             super.handleRequest(request, response);   
  24.         } finally {   
  25.             ServiceContext.end();   
  26.         }   
  27.     }   
  28.        
  29. }  
/**
 * 
 *
 * @author liliang
 * @descirption 对spring的HessianServiceExporter进行扩展,以能够在逻辑service中使用httpRequest
 */
public class HessianServiceContextExporter extends HessianServiceExporter {
	
	@Override
	public void handleRequest(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		try {
			//
			String serviceId = request.getPathInfo();
			String objectId = request.getParameter("id");
			if (objectId == null)
				objectId = request.getParameter("ejbid");

			ServiceContext.begin(request, serviceId, objectId);

			// 调用spring自身的实现
			super.handleRequest(request, response);
		} finally {
			ServiceContext.end();
		}
	}
	
}


而spring mvc的servlet配置文件中如下:
Java代码 复制代码  收藏代码
  1. <bean name="/hello.service"  
  2.     class="org.springframework.remoting.caucho.HessianServiceContextExporter">   
  3.     <property name="service" ref="hellowordService" />   
  4.     <property name="serviceInterface"  
  5.         value="com.mostar.sng.service.sample.IHellowordService" />   
  6. </bean>  
	<bean name="/hello.service"
		class="org.springframework.remoting.caucho.HessianServiceContextExporter">
		<property name="service" ref="hellowordService" />
		<property name="serviceInterface"
			value="com.mostar.sng.service.sample.IHellowordService" />
	</bean>


至此,即便我们使用spring进行发布hessian服务,通过可以通过ServiceContext获取上下文的request等信息了。
以上不论对于hessian client还是对于spring的hessian封装,都是只做增量扩展。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值