项目选定Hessian作为web service的实现方式,确实很轻量级,速度就跟直接用socket差不多,全是二进制传送节约了不少开销。但是在使用过程中有业务需要是必须获得远程端的ip地址,主机名等信息的。翻便Hessian的文档和google了n次未果,迫不得已到caucho和spring论坛去问,都没有得到答复。今天心一横把hessian的源代码加入到项目中单步跟踪,总算有点小收获。献丑分享出来,一方面给需要的朋友,主要还是希望各位找找是否存在bug,以及是否有更好的改良。
一:先撇开Spring不谈,来看看纯Hessian的调用
按照hessian文档里边介绍的demo,在web.xml里边如下配置
- <servlet>
- <servlet-name>hello</servlet-name>
- <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
- <init-param>
- <param-name>home-class</param-name>
- <param-value>example.BasicService</param-value>
- </init-param>
- <init-param>
- <param-name>home-api</param-name>
- <param-value>example.Basic</param-value>
- </init-param>
- </servlet>
- <servlet-mapping>
- <url-pattern>/hello</url-pattern>
- <servlet-name>hello</servlet-name>
- </servlet-mapping>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
<init-param>
<param-name>home-class</param-name>
<param-value>example.BasicService</param-value>
</init-param>
<init-param>
<param-name>home-api</param-name>
<param-value>example.Basic</param-value>
</init-param>
</servlet>
<servlet-mapping>
<url-pattern>/hello</url-pattern>
<servlet-name>hello</servlet-name>
</servlet-mapping>
由此可知Hessian调用的入口是HessianServlet这个Servlet,进去看看
- /**
- * Servlet for serving Hessian services.
- */
- public class HessianServlet extends GenericServlet {
- private Class _homeAPI;
- private Object _homeImpl;
- private Class _objectAPI;
- private Object _objectImpl;
- private HessianSkeleton _homeSkeleton;
- private HessianSkeleton _objectSkeleton;
- private SerializerFactory _serializerFactory;
- public String getServletInfo()
- {
- return "Hessian Servlet";
- }
- /**
- * Sets the home api.
- */
- public void setHomeAPI(Class api)
- {
- _homeAPI = api;
- }
- /**
- * Sets the home implementation
- */
- public void setHome(Object home)
- {
- _homeImpl = home;
- }
- /**
- * Sets the object api.
- */
- public void setObjectAPI(Class api)
- {
- _objectAPI = api;
- }
- /**
- * Sets the object implementation
- */
- public void setObject(Object object)
- {
- _objectImpl = object;
- }
- /**
- * Sets the service class.
- */
- public void setService(Object service)
- {
- setHome(service);
- }
- /**
- * Sets the api-class.
- */
- public void setAPIClass(Class api)
- {
- setHomeAPI(api);
- }
- /**
- * Gets the api-class.
- */
- public Class getAPIClass()
- {
- return _homeAPI;
- }
- /**
- * Sets the serializer factory.
- */
- public void setSerializerFactory(SerializerFactory factory)
- {
- _serializerFactory = factory;
- }
- /**
- * Gets the serializer factory.
- */
- public SerializerFactory getSerializerFactory()
- {
- if (_serializerFactory == null)
- _serializerFactory = new SerializerFactory();
- return _serializerFactory;
- }
- /**
- * Sets the serializer send collection java type.
- */
- public void setSendCollectionType(boolean sendType)
- {
- getSerializerFactory().setSendCollectionType(sendType);
- }
- /**
- * Initialize the service, including the service object.
- */
- public void init(ServletConfig config)
- throws ServletException
- {
- super.init(config);
- try {
- if (_homeImpl != null) {
- }
- else if (getInitParameter("home-class") != null) {
- String className = getInitParameter("home-class");
- Class homeClass = loadClass(className);
- _homeImpl = homeClass.newInstance();
- init(_homeImpl);
- }
- else if (getInitParameter("service-class") != null) {
- String className = getInitParameter("service-class");
- Class homeClass = loadClass(className);
- _homeImpl = homeClass.newInstance();
- init(_homeImpl);
- }
- else {
- if (getClass().equals(HessianServlet.class))
- throw new ServletException("server must extend HessianServlet");
- _homeImpl = this;
- }
- if (_homeAPI != null) {
- }
- else if (getInitParameter("home-api") != null) {
- String className = getInitParameter("home-api");
- _homeAPI = loadClass(className);
- }
- else if (getInitParameter("api-class") != null) {
- String className = getInitParameter("api-class");
- _homeAPI = loadClass(className);
- }
- else if (_homeImpl != null) {
- _homeAPI = findRemoteAPI(_homeImpl.getClass());
- if (_homeAPI == null)
- _homeAPI = _homeImpl.getClass();
- }
- if (_objectImpl != null) {
- }
- else if (getInitParameter("object-class") != null) {
- String className = getInitParameter("object-class");
- Class objectClass = loadClass(className);
- _objectImpl = objectClass.newInstance();
- init(_objectImpl);
- }
- if (_objectAPI != null) {
- }
- else if (getInitParameter("object-api") != null) {
- String className = getInitParameter("object-api");
- _objectAPI = loadClass(className);
- }
- else if (_objectImpl != null)
- _objectAPI = _objectImpl.getClass();
- _homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
- if (_objectAPI != null)
- _homeSkeleton.setObjectClass(_objectAPI);
- if (_objectImpl != null) {
- _objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);
- _objectSkeleton.setHomeClass(_homeAPI);
- }
- else
- _objectSkeleton = _homeSkeleton;
- } catch (ServletException e) {
- throw e;
- } catch (Exception e) {
- throw new ServletException(e);
- }
- }
- private Class findRemoteAPI(Class implClass)
- {
- if (implClass == null || implClass.equals(GenericService.class))
- return null;
- Class []interfaces = implClass.getInterfaces();
- if (interfaces.length == 1)
- return interfaces[0];
- return findRemoteAPI(implClass.getSuperclass());
- }
- private Class loadClass(String className)
- throws ClassNotFoundException
- {
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- if (loader != null)
- return Class.forName(className, false, loader);
- else
- return Class.forName(className);
- }
- private void init(Object service)
- throws ServletException
- {
- if (service instanceof Service)
- ((Service) service).init(getServletConfig());
- else if (service instanceof Servlet)
- ((Servlet) service).init(getServletConfig());
- }
- /**
- * Execute a request. The path-info of the request selects the bean.
- * Once the bean's selected, it will be applied.
- */
- public void service(ServletRequest request, ServletResponse response)
- throws IOException, ServletException
- {
- HttpServletRequest req = (HttpServletRequest) request;
- HttpServletResponse res = (HttpServletResponse) response;
- if (! req.getMethod().equals("POST")) {
- res.setStatus(500, "Hessian Requires POST");
- PrintWriter out = res.getWriter();
- res.setContentType("text/html");
- out.println("<h1>Hessian Requires POST</h1>");
- return;
- }
- String serviceId = req.getPathInfo();
- String objectId = req.getParameter("id");
- if (objectId == null)
- objectId = req.getParameter("ejbid");
- ServiceContext.begin(req, serviceId, objectId);
- try {
- InputStream is = request.getInputStream();
- OutputStream os = response.getOutputStream();
- Hessian2Input in = new Hessian2Input(is);
- AbstractHessianOutput out;
- SerializerFactory serializerFactory = getSerializerFactory();
- in.setSerializerFactory(serializerFactory);
- int code = in.read();
- if (code != 'c') {
- // XXX: deflate
- throw new IOException("expected 'c' in hessian input at " + code);
- }
- int major = in.read();
- int minor = in.read();
- if (major >= 2)
- out = new Hessian2Output(os);
- else
- out = new HessianOutput(os);
- out.setSerializerFactory(serializerFactory);
- if (objectId != null)
- _objectSkeleton.invoke(in, out);
- else
- _homeSkeleton.invoke(in, out);
- out.close();
- } catch (RuntimeException e) {
- throw e;
- } catch (ServletException e) {
- throw e;
- } catch (Throwable e) {
- throw new ServletException(e);
- } finally {
- ServiceContext.end();
- }
- }
- }
/**
* Servlet for serving Hessian services.
*/
public class HessianServlet extends GenericServlet {
private Class _homeAPI;
private Object _homeImpl;
private Class _objectAPI;
private Object _objectImpl;
private HessianSkeleton _homeSkeleton;
private HessianSkeleton _objectSkeleton;
private SerializerFactory _serializerFactory;
public String getServletInfo()
{
return "Hessian Servlet";
}
/**
* Sets the home api.
*/
public void setHomeAPI(Class api)
{
_homeAPI = api;
}
/**
* Sets the home implementation
*/
public void setHome(Object home)
{
_homeImpl = home;
}
/**
* Sets the object api.
*/
public void setObjectAPI(Class api)
{
_objectAPI = api;
}
/**
* Sets the object implementation
*/
public void setObject(Object object)
{
_objectImpl = object;
}
/**
* Sets the service class.
*/
public void setService(Object service)
{
setHome(service);
}
/**
* Sets the api-class.
*/
public void setAPIClass(Class api)
{
setHomeAPI(api);
}
/**
* Gets the api-class.
*/
public Class getAPIClass()
{
return _homeAPI;
}
/**
* Sets the serializer factory.
*/
public void setSerializerFactory(SerializerFactory factory)
{
_serializerFactory = factory;
}
/**
* Gets the serializer factory.
*/
public SerializerFactory getSerializerFactory()
{
if (_serializerFactory == null)
_serializerFactory = new SerializerFactory();
return _serializerFactory;
}
/**
* Sets the serializer send collection java type.
*/
public void setSendCollectionType(boolean sendType)
{
getSerializerFactory().setSendCollectionType(sendType);
}
/**
* Initialize the service, including the service object.
*/
public void init(ServletConfig config)
throws ServletException
{
super.init(config);
try {
if (_homeImpl != null) {
}
else if (getInitParameter("home-class") != null) {
String className = getInitParameter("home-class");
Class homeClass = loadClass(className);
_homeImpl = homeClass.newInstance();
init(_homeImpl);
}
else if (getInitParameter("service-class") != null) {
String className = getInitParameter("service-class");
Class homeClass = loadClass(className);
_homeImpl = homeClass.newInstance();
init(_homeImpl);
}
else {
if (getClass().equals(HessianServlet.class))
throw new ServletException("server must extend HessianServlet");
_homeImpl = this;
}
if (_homeAPI != null) {
}
else if (getInitParameter("home-api") != null) {
String className = getInitParameter("home-api");
_homeAPI = loadClass(className);
}
else if (getInitParameter("api-class") != null) {
String className = getInitParameter("api-class");
_homeAPI = loadClass(className);
}
else if (_homeImpl != null) {
_homeAPI = findRemoteAPI(_homeImpl.getClass());
if (_homeAPI == null)
_homeAPI = _homeImpl.getClass();
}
if (_objectImpl != null) {
}
else if (getInitParameter("object-class") != null) {
String className = getInitParameter("object-class");
Class objectClass = loadClass(className);
_objectImpl = objectClass.newInstance();
init(_objectImpl);
}
if (_objectAPI != null) {
}
else if (getInitParameter("object-api") != null) {
String className = getInitParameter("object-api");
_objectAPI = loadClass(className);
}
else if (_objectImpl != null)
_objectAPI = _objectImpl.getClass();
_homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
if (_objectAPI != null)
_homeSkeleton.setObjectClass(_objectAPI);
if (_objectImpl != null) {
_objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);
_objectSkeleton.setHomeClass(_homeAPI);
}
else
_objectSkeleton = _homeSkeleton;
} catch (ServletException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}
private Class findRemoteAPI(Class implClass)
{
if (implClass == null || implClass.equals(GenericService.class))
return null;
Class []interfaces = implClass.getInterfaces();
if (interfaces.length == 1)
return interfaces[0];
return findRemoteAPI(implClass.getSuperclass());
}
private Class loadClass(String className)
throws ClassNotFoundException
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader != null)
return Class.forName(className, false, loader);
else
return Class.forName(className);
}
private void init(Object service)
throws ServletException
{
if (service instanceof Service)
((Service) service).init(getServletConfig());
else if (service instanceof Servlet)
((Servlet) service).init(getServletConfig());
}
/**
* Execute a request. The path-info of the request selects the bean.
* Once the bean's selected, it will be applied.
*/
public void service(ServletRequest request, ServletResponse response)
throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (! req.getMethod().equals("POST")) {
res.setStatus(500, "Hessian Requires POST");
PrintWriter out = res.getWriter();
res.setContentType("text/html");
out.println("<h1>Hessian Requires POST</h1>");
return;
}
String serviceId = req.getPathInfo();
String objectId = req.getParameter("id");
if (objectId == null)
objectId = req.getParameter("ejbid");
ServiceContext.begin(req, serviceId, objectId);
try {
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();
Hessian2Input in = new Hessian2Input(is);
AbstractHessianOutput out;
SerializerFactory serializerFactory = getSerializerFactory();
in.setSerializerFactory(serializerFactory);
int code = in.read();
if (code != 'c') {
// XXX: deflate
throw new IOException("expected 'c' in hessian input at " + code);
}
int major = in.read();
int minor = in.read();
if (major >= 2)
out = new Hessian2Output(os);
else
out = new HessianOutput(os);
out.setSerializerFactory(serializerFactory);
if (objectId != null)
_objectSkeleton.invoke(in, out);
else
_homeSkeleton.invoke(in, out);
out.close();
} catch (RuntimeException e) {
throw e;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
throw new ServletException(e);
} finally {
ServiceContext.end();
}
}
}
先看init()函数,功能还是一样,初始话一些东西,读入init-param的内容,并且load这些init-param的class
主要的还是service()函数
在service函数里边会获得request和response对象的输入和输出流,用来构造Hessian2Input和Hessian2Output,Hessian就是解析这两个东西来执行函数调用的。当然,在service里边还有一个重要的语句
- ServiceContext.begin(req, serviceId, objectId);
ServiceContext.begin(req, serviceId, objectId);
这个函数有点奇怪,我每次到这里serviceId和objectId都是空,不知道是不是历史遗留问题还存在这两个参数。
进去这个类看看
- public class ServiceContext {
- private static final ThreadLocal _localContext = new ThreadLocal();
- private ServletRequest _request;
- private String _serviceName;
- private String _objectId;
- private int _count;
- private HashMap _headers = new HashMap();
- private ServiceContext()
- {
- }
- /**
- * Sets the request object prior to calling the service's method.
- *
- * @param request the calling servlet request
- * @param serviceId the service identifier
- * @param objectId the object identifier
- */
- public static void begin(ServletRequest request,
- String serviceName,
- String objectId)
- throws ServletException
- {
- ServiceContext context = (ServiceContext) _localContext.get();
- if (context == null) {
- context = new ServiceContext();
- _localContext.set(context);
- }
- context._request = request;
- context._serviceName = serviceName;
- context._objectId = objectId;
- context._count++;
- }
- /**
- * Returns the service request.
- */
- public static ServiceContext getContext()
- {
- return (ServiceContext) _localContext.get();
- }
- /**
- * Adds a header.
- */
- public void addHeader(String header, Object value)
- {
- _headers.put(header, value);
- }
- /**
- * Gets a header.
- */
- public Object getHeader(String header)
- {
- return _headers.get(header);
- }
- /**
- * Gets a header from the context.
- */
- public static Object getContextHeader(String header)
- {
- ServiceContext context = (ServiceContext) _localContext.get();
- if (context != null)
- return context.getHeader(header);
- else
- return null;
- }
- /**
- * Returns the service request.
- */
- public static ServletRequest getContextRequest()
- {
- ServiceContext context = (ServiceContext) _localContext.get();
- if (context != null)
- return context._request;
- else
- return null;
- }
- /**
- * Returns the service id, corresponding to the pathInfo of the URL.
- */
- public static String getContextServiceName()
- {
- ServiceContext context = (ServiceContext) _localContext.get();
- if (context != null)
- return context._serviceName;
- else
- return null;
- }
- /**
- * Returns the object id, corresponding to the ?id= of the URL.
- */
- public static String getContextObjectId()
- {
- ServiceContext context = (ServiceContext) _localContext.get();
- if (context != null)
- return context._objectId;
- else
- return null;
- }
- /**
- * Cleanup at the end of a request.
- */
- public static void end()
- {
- ServiceContext context = (ServiceContext) _localContext.get();
- if (context != null && --context._count == 0) {
- context._request = null;
- context._headers.clear();
- }
- }
- /**
- * Returns the service request.
- *
- * @deprecated
- */
- public static ServletRequest getRequest()
- {
- ServiceContext context = (ServiceContext) _localContext.get();
- if (context != null)