我们知道HTTP是一种超文本传输协议,其中两个比较重要的对象是HttpRequest和HttpResponse,HTTP协议可以实现文本内容的传输和文件传输。本文将通过HTTP的文件传输实现远程调用对象功能。
由于最近在做一个定时器集群管理项目,需要远程去控制定时器应用内部的定时器运行状态,比如获取当前应用的所有TimerJOb的实例对象,比如对某个定时器任务进行暂停,重启以及重新调整定时器运行参数,例如:将每小时运行一次调整为每两个小时运行一次,而不需要重启远程的应用。在这个过程中需要运用到远程调用对象的功能,该项目提供了两种方式实现该功能,一种是RPC,另一种是HTTP。本文将主要对HTTP的实现进行描述。
首选先描述一下我的实现思路。通过java内部的InvocationHandler和Proxy进行产生远程对象接口的一个代理对象,于是在InvocationHandler内部就可以拦截代理对象调用的每个方法,那么在拦截方法调用后,将调用的方法名称,参数以及调用接口信息封装成一个调用的对象,并且将该对象进行序列化,通过HTTP的文件参数到远程的应用上面,远程应用在通过反序列化将解析出需要调用的那个类以及哪个方法,并且调用执行,然后将执行结果通过Response的writer写回,这样就可以在调用者完全不知道的情况下,去远程访问某个对象以及返回执行结果,使用过程和调用本地方法一样。下面将对具体实现进行描述。
为了实现这一功能,我创建一个Helper类,该类结构如下:
public class HttpRequestHelper implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = -600087221405366543L; private String methodName; private Object[] args; private String url; /** * @return the methodName */ public String getMethodName() { return methodName; } /** * @return the args */ public Object[] getArgs() { return args; } /** * @param methodName * the methodName to set */ private HttpRequestHelper setMethodName(String methodName) { this.methodName = methodName; return this; } /** * @param args * the args to set */ private HttpRequestHelper setArgs(Object[] args) { this.args = args; return this; } /** * @param url * the url to set */ private HttpRequestHelper setUrl(String url) { this.url = url; return this; } @SuppressWarnings("unchecked") public static <T extends Object> T getProxy(Class<T> serviceInterface, final String url) { InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { HttpRequestHelper requestHelper = new HttpRequestHelper(); requestHelper.setArgs(args).setUrl(url) .setMethodName(method.getName()); Object result = requestHelper.invoke(); if (Exception.class.isAssignableFrom(result.getClass())) { Exception e = (Exception) result; throw e; } return result; } }; return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[] { serviceInterface }, handler); } private Object invoke() throws IOException, ClassNotFoundException { HttpClient client = new HttpClient(new HttpClientParams(), new SimpleHttpConnectionManager(true)); PostMethod post = new PostMethod(this.url); post.getParams().setContentCharset("UTF-8"); try { byte[] bytes = serializableObject(this); File tempFile = writeObjectToTempFile(bytes); FilePart filePart = new FilePart("tempFile", tempFile.getName(), tempFile); Part[] parts = new Part[1]; parts[0] = filePart; MultipartRequestEntity mrp = new MultipartRequestEntity(parts, post.getParams()); post.setRequestEntity(mrp); client.executeMethod(post); tempFile.deleteOnExit(); return unSerializableObject(post.getResponseBody()); } catch (Exception e) { return e; } finally { post.releaseConnection(); } } private static File writeObjectToTempFile(byte[] contents) throws IOException { String fileName = String.valueOf(System.currentTimeMillis()); File tempFile = new File(HttpRequestHelper.class.getResource("/").getPath() + fileName); FileOutputStream outputStream = new FileOutputStream(tempFile); outputStream.write(contents, 0, contents.length); outputStream.flush(); outputStream.close(); return tempFile; } private static byte[] serializableObject(Object object) throws IOException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream); out.writeObject(object); return byteArrayOutputStream.toByteArray(); } private static Object unSerializableObject(byte[] bytes) throws IOException, ClassNotFoundException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( bytes); ObjectInputStream input = new ObjectInputStream(byteArrayInputStream); return input.readObject(); } }
该类只对外暴露了一个静态方法(除了get方法)getProxy,该方法便是为你要调用远程接口创建一个代理对象,远程地址则是你通过getProxy方法设置进来,在该方法体内通过jdk自身的InvocationHandler来拦截这个代理对象请求的每个方法,当通过代理对象调用某个方法时,则会拦截请求的方法对象,请求的参数,然后将信息封装到HttpRequestHelper中,将HttpRequestHelper对象进行序列化,并且将序列化的字节数组写到一个临时文件,然后通过对这个临时文件传输到远程的服务,便达到了将HttpRequestHelper对象传输到远程服务,远程服务再通过反序列化HttpRequestHelper对象,便知道是调用哪个服务,以及调用服务的哪个方法,然后执行对应服务的对应方法,将结果再进行序列化成字节数组,通过response的wirter将结果写回请求应用,这边完成了整个的请求过程。对于远程服务处理请求代码,下面粘贴处一个Filter的实现类,用于接受该类请求
public boolean service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; OutputStream outputStream = res.getOutputStream(); req.setCharacterEncoding("UTF-8"); DemoService slaveEndService = new DemoServiceImpl(); byte[] content = null; try { content = readHttpObjectByte(request); } catch (Exception e1) { e1.printStackTrace(); } try { HttpRequestHelper requestHelper = (HttpRequestHelper) unSerializableObject(content); Object result = slaveEndService.serviceDispatche( requestHelper.getMethodName(), requestHelper.getArgs()); outputStream.write(serializableObject(result)); outputStream.flush(); } catch (ClassNotFoundException e) { outputStream.write(serializableObject(e)); outputStream.flush(); } finally { outputStream.close(); } return true; } @SuppressWarnings("rawtypes") private static byte[] readHttpObjectByte(HttpServletRequest request) { boolean isMultipart = ServletFileUpload.isMultipartContent(request); byte[] content = null; if (isMultipart) { FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); try { Iterator items = upload.parseRequest(request).iterator(); while (items.hasNext()) { FileItem item = (FileItem) items.next(); if (!item.isFormField()) { InputStream is = item.getInputStream(); ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); byte[] buff = new byte[1024]; int off = 0; while ((off = is.read(buff, 0, 1024)) != -1) { arrayOutputStream.write(buff, 0, off); } content = arrayOutputStream.toByteArray(); arrayOutputStream.flush(); arrayOutputStream.close(); is.close(); } } } catch (Exception e) { } } return content; } /* * (non-Javadoc) * * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, * javax.servlet.ServletResponse, javax.servlet.FilterChain) */ @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if (!service(req, res)) { chain.doFilter(req, res); } }