1. Caucho
1.1 概况
spring-remoting代码的情况如下:
本节近分析caucho模块。
1.2 分类
其中以hession为例,Hessian远程服务调用过程:
Hessian远程服务调用过程
1.2.1 客户端
BurlapProxyFactoryBean,BurlapClientInterceptor;
HessianProxyFactoryBean,HessianClientInterceptor;
HessianProxyFactoryBean继承自HessianClientInterceptor,间接封装了HessianProxyFactory。HessianProxyFactory是hessian的client实现类,
示例:
public interface Basic { public String hello(); } import com.caucho.hessian.client.HessianProxyFactory; public class BasicClient { public static void main(String []args) throws Exception { String url = "http://www.caucho.com/hessian/test/basic"; HessianProxyFactory factory = new HessianProxyFactory(); Basic basic = (Basic) factory.create(Basic.class, url); System.out.println("Hello: " + basic.hello()); } }
create方法如下:
/** * Creates a new proxy with the specified URL. The returned object * is a proxy with the interface specified by api. * * <pre> * String url = "http://localhost:8080/ejb/hello"); * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url); * </pre> * * @param api the interface the proxy class needs to implement * @param url the URL where the client object is located. * * @return a proxy to the object with the specified interface. */ 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 HessianProxy(url, this, api); return Proxy.newProxyInstance(loader, new Class[] { api, HessianRemoteObject.class }, handler); }
其中HessianProxy实现了java的动态代理
/** * Proxy implementation for Hessian clients. Applications will generally * use HessianProxyFactory to create proxy clients. */ public class HessianProxy implements InvocationHandler, Serializable { private static final Logger log = Logger.getLogger(HessianProxy.class.getName()); protected HessianProxyFactory _factory; private WeakHashMap<Method,String> _mangleMap = new WeakHashMap<Method,String>(); private Class<?> _type; private URL _url; /** * Protected constructor for subclassing */ protected HessianProxy(URL url, HessianProxyFactory factory) { this(url, factory, null); } /** * Protected constructor for subclassing */ protected HessianProxy(URL url, HessianProxyFactory factory, Class<?> type) { _factory = factory; _url = url; _type = type; } }
最重要的invoke方法如下:
/** * Handles the object invocation. * * @param proxy the proxy object to invoke * @param method the method to call * @param args the arguments to the proxy object */ public Object invoke(Object proxy, Method method, Object []args) throws Throwable { String mangleName; synchronized (_mangleMap) { mangleName = _mangleMap.get(method); } if (mangleName == null) { String methodName = method.getName(); Class<?> []params = method.getParameterTypes(); // equals and hashCode are special cased if (methodName.equals("equals") && params.length == 1 && params[0].equals(Object.class)) { Object value = args[0]; if (value == null || ! Proxy.isProxyClass(value.getClass())) return Boolean.FALSE; Object proxyHandler = Proxy.getInvocationHandler(value); if (! (proxyHandler instanceof HessianProxy)) return Boolean.FALSE; HessianProxy handler = (HessianProxy) proxyHandler; return new Boolean(_url.equals(handler.getURL())); } else if (methodName.equals("hashCode") && params.length == 0) return new Integer(_url.hashCode()); else if (methodName.equals("getHessianType")) return proxy.getClass().getInterfaces()[0].getName(); else if (methodName.equals("getHessianURL")) return _url.toString(); else if (methodName.equals("toString") && params.length == 0) return "HessianProxy[" + _url + "]"; if (! _factory.isOverloadEnabled()) mangleName = method.getName(); else mangleName = mangleName(method); synchronized (_mangleMap) { _mangleMap.put(method, mangleName); } } InputStream is = null; HessianConnection conn = null; try { if (log.isLoggable(Level.FINER)) log.finer("Hessian[" + _url + "] calling " + mangleName); conn = sendRequest(mangleName, args); is = getInputStream(conn); if (log.isLoggable(Level.FINEST)) { PrintWriter dbg = new PrintWriter(new LogWriter(log)); HessianDebugInputStream dIs = new HessianDebugInputStream(is, dbg); dIs.startTop2(); is = dIs; } AbstractHessianInput in; int code = is.read(); if (code == 'H') { int major = is.read(); int minor = is.read(); in = _factory.getHessian2Input(is); Object value = in.readReply(method.getReturnType()); return value; } else if (code == 'r') { int major = is.read(); int minor = is.read(); in = _factory.getHessianInput(is); in.startReplyBody(); Object value = in.readObject(method.getReturnType()); if (value instanceof InputStream) { value = new ResultInputStream(conn, is, in, (InputStream) value); is = null; conn = null; } else in.completeReply(); return value; } else throw new HessianProtocolException("'" + (char) code + "' is an unknown code"); } catch (HessianProtocolException e) { throw new HessianRuntimeException(e); } finally { try { if (is != null) is.close(); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } try { if (conn != null) conn.destroy(); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } } }
发送http请求
/** * Sends the HTTP request to the Hessian connection. */ protected HessianConnection sendRequest(String methodName, Object []args) throws IOException { HessianConnection conn = null; conn = _factory.getConnectionFactory().open(_url); boolean isValid = false; try { addRequestHeaders(conn); OutputStream os = null; try { os = conn.getOutputStream(); } catch (Exception e) { throw new HessianRuntimeException(e); } if (log.isLoggable(Level.FINEST)) { PrintWriter dbg = new PrintWriter(new LogWriter(log)); HessianDebugOutputStream dOs = new HessianDebugOutputStream(os, dbg); dOs.startTop2(); os = dOs; } AbstractHessianOutput out = _factory.getHessianOutput(os); out.call(methodName, args); out.flush(); conn.sendRequest(); isValid = true; return conn; } finally { if (! isValid && conn != null) conn.destroy(); } }
创建http连接代码
/** * Opens a new or recycled connection to the HTTP server. */ 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 = _proxyFactory.getConnectTimeout(); if (connectTimeout >= 0) conn.setConnectTimeout((int) connectTimeout); conn.setDoOutput(true); long readTimeout = _proxyFactory.getReadTimeout(); if (readTimeout > 0) { try { conn.setReadTimeout((int) readTimeout); } catch (Throwable e) { } }
1.2.2 服务器端
HessianExporter及其实现类HessianServiceExporter,SimpleHessianServiceExporter.
hessian服务端示例
package hessian.test;
import com.caucho.hessian.server.HessianServlet;
public class BasicService extends HessianServlet implements Basic {
public String hello()
{
return "Hello, world";
}
}
我们来看一下:
HessianServiceExporter
/** * Servlet-API-based HTTP request handler that exports the specified service bean * as Hessian service endpoint, accessible via a Hessian proxy. * * <p><b>Note:</b> Spring also provides an alternative version of this exporter, * for Sun's JRE 1.6 HTTP server: {@link SimpleHessianServiceExporter}. * * <p>Hessian is a slim, binary RPC protocol. * For information on Hessian, see the * <a href="http://www.caucho.com/hessian">Hessian website</a>. * <b>Note: As of Spring 4.0, this exporter requires Hessian 4.0 or above.</b> * * <p>Hessian services exported with this class can be accessed by * any Hessian client, as there isn't any special handling involved. * * @author Juergen Hoeller * @since 13.05.2003 * @see HessianClientInterceptor * @see HessianProxyFactoryBean * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter * @see org.springframework.remoting.rmi.RmiServiceExporter */
处理客户端请求的方法:
/**
* Processes the incoming Hessian request and creates a Hessian response.
*/
@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 {
invoke(request.getInputStream(), response.getOutputStream());
}
catch (Throwable ex) {
throw new NestedServletException("Hessian skeleton invocation failed", ex);
}
}
invoke调用
/**
* Actually invoke the skeleton with the given streams.
* @param skeleton the skeleton to invoke
* @param inputStream the request stream
* @param outputStream the response stream
* @throws Throwable if invocation failed
*/
protected void doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)
throws Throwable {
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
try {
InputStream isToUse = inputStream;
OutputStream osToUse = outputStream;
if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) {
PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger));
@SuppressWarnings("resource")
HessianDebugInputStream dis = new HessianDebugInputStream(inputStream, debugWriter);
@SuppressWarnings("resource")
HessianDebugOutputStream dos = new HessianDebugOutputStream(outputStream, debugWriter);
dis.startTop2();
dos.startTop2();
isToUse = dis;
osToUse = dos;
}
if (!isToUse.markSupported()) {
isToUse = new BufferedInputStream(isToUse);
isToUse.mark(1);
}
int code = isToUse.read();
int major;
int minor;
AbstractHessianInput in;
AbstractHessianOutput out;
if (code == 'H') {
// Hessian 2.0 stream
major = isToUse.read();
minor = isToUse.read();
if (major != 0x02) {
throw new IOException("Version " + major + "." + minor + " is not understood");
}
in = new Hessian2Input(isToUse);
out = new Hessian2Output(osToUse);
in.readCall();
}
else if (code == 'C') {
// Hessian 2.0 call... for some reason not handled in HessianServlet!
isToUse.reset();
in = new Hessian2Input(isToUse);
out = new Hessian2Output(osToUse);
in.readCall();
}
else if (code == 'c') {
// Hessian 1.0 call
major = isToUse.read();
minor = isToUse.read();
in = new HessianInput(isToUse);
if (major >= 2) {
out = new Hessian2Output(osToUse);
}
else {
out = new HessianOutput(osToUse);
}
}
else {
throw new IOException("Expected 'H'/'C' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code);
}
if (this.serializerFactory != null) {
in.setSerializerFactory(this.serializerFactory);
out.setSerializerFactory(this.serializerFactory);
}
if (this.remoteResolver != null) {
in.setRemoteResolver(this.remoteResolver);
}
try {
skeleton.invoke(in, out);
}
finally {
try {
in.close();
isToUse.close();
}
catch (IOException ex) {
// ignore
}
try {
out.close();
osToUse.close();
}
catch (IOException ex) {
// ignore
}
}
}
finally {
resetThreadContextClassLoader(originalClassLoader);
}
}
调用skeleton的invoke方法
/**
* Invoke the object with the request from the input stream.
*
* @param in the Hessian input stream
* @param out the Hessian output stream
*/
public void invoke(Object service,
AbstractHessianInput in,
AbstractHessianOutput out)
throws Exception
{
ServiceContext context = ServiceContext.getContext();
// backward compatibility for some frameworks that don't read
// the call type first
in.skipOptionalCall();
// Hessian 1.0 backward compatibility
String header;
while ((header = in.readHeader()) != null) {
Object value = in.readObject();
context.addHeader(header, value);
}
String methodName = in.readMethod();
int argLength = in.readMethodArgLength();
Method method;
method = getMethod(methodName + "__" + argLength);
if (method == null)
method = getMethod(methodName);
if (method != null) {
}
else if ("_hessian_getAttribute".equals(methodName)) {
String attrName = in.readString();
in.completeCall();
String value = null;
if ("java.api.class".equals(attrName))
value = getAPIClassName();
else if ("java.home.class".equals(attrName))
value = getHomeClassName();
else if ("java.object.class".equals(attrName))
value = getObjectClassName();
out.writeReply(value);
out.close();
return;
}
else if (method == null) {
out.writeFault("NoSuchMethodException",
escapeMessage("The service has no method named: " + in.getMethod()),
null);
out.close();
return;
}
Class<?> []args = method.getParameterTypes();
if (argLength != args.length && argLength >= 0) {
out.writeFault("NoSuchMethod",
escapeMessage("method " + method + " argument length mismatch, received length=" + argLength),
null);
out.close();
return;
}
Object []values = new Object[args.length];
for (int i = 0; i < args.length; i++) {
// XXX: needs Marshal object
values[i] = in.readObject(args[i]);
}
Object result = null;
try {
result = method.invoke(service, values);
} catch (Exception e) {
Throwable e1 = e;
if (e1 instanceof InvocationTargetException)
e1 = ((InvocationTargetException) e).getTargetException();
log.log(Level.FINE, this + " " + e1.toString(), e1);
out.writeFault("ServiceException",
escapeMessage(e1.getMessage()),
e1);
out.close();
return;
}
// The complete call needs to be after the invoke to handle a
// trailing InputStream
in.completeCall();
out.writeReply(result);
out.close();
}
反射触发类的方法。
BurlapExporter及其实现类BurlapServiceExporter,SimpleBurlapServiceExporter,因已经depressed,故略。
1.3 小结
Spring封装了hessian客户端和服务端的通用代码,把实现者和调用者作为bean放到spring容器中管理,简化了开发。分析源码的过程中,发现在客户端使用了动态代理,在服务端使用反射,让我们加深了对java基础知识的理解。