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. * ** String url = "http://localhost:8080/ejb/hello"); * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url); ** * @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_mangleMap = new WeakHashMap (); 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. * *Note: Spring also provides an alternative version of this exporter, * for Sun's JRE 1.6 HTTP server: {@link SimpleHessianServiceExporter}. * *
Hessian is a slim, binary RPC protocol. * For information on Hessian, see the * Hessian website. * Note: As of Spring 4.0, this exporter requires Hessian 4.0 or above. * *
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基础知识的理解。