001 package biz.hammurapi.remoting.http; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.ByteArrayOutputStream; 005 import java.io.IOException; 006 import java.io.ObjectInputStream; 007 import java.io.ObjectOutputStream; 008 import java.lang.reflect.InvocationHandler; 009 import java.lang.reflect.Method; 010 import java.lang.reflect.Proxy; 011 import java.util.zip.GZIPInputStream; 012 import java.util.zip.GZIPOutputStream; 013 014 import org.apache.commons.httpclient.HttpClient; 015 import org.apache.commons.httpclient.HttpConnectionManager; 016 import org.apache.commons.httpclient.HttpStatus; 017 import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; 018 import org.apache.commons.httpclient.methods.PostMethod; 019 020 import biz.hammurapi.remoting.Invocation; 021 022 public class HttpRemoteInvocationHandler implements InvocationHandler { 023 024 private HttpReference reference; 025 private HttpConnectionManager connectionManager; 026 private String cookie; 027 private ClassLoader classLoader; 028 029 public HttpRemoteInvocationHandler(HttpReference reference, 030 String cookie, 031 HttpConnectionManager connectionManager, 032 ClassLoader classLoader) { 033 this.reference = reference; 034 this.connectionManager = connectionManager; 035 this.cookie = cookie; 036 this.classLoader = classLoader; 037 } 038 039 /** 040 * Used for remote class and resource loading. 041 * @param name 042 * @return 043 */ 044 public byte[] getResource(String resourceName) { 045 try { 046 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 047 ObjectOutputStream oos = new ObjectOutputStream(new GZIPOutputStream(baos)); 048 oos.writeObject(resourceName); 049 oos.close(); 050 HttpClient httpClient = new HttpClient(connectionManager); 051 PostMethod httpMethod = new PostMethod(reference.getRemoteUrl()); 052 try { 053 httpMethod.setRequestEntity(new ByteArrayRequestEntity(baos.toByteArray())); 054 if (cookie!=null) { 055 httpMethod.setRequestHeader("Cookie", cookie); 056 } 057 int statusCode = httpClient.executeMethod(httpMethod); 058 if (statusCode == HttpStatus.SC_OK) { 059 return httpMethod.getResponseBody(); 060 } else if (statusCode == HttpStatus.SC_NOT_FOUND) { 061 return null; 062 } 063 throw new HttpRemoteException("Remoting problem, HTTP error code: "+statusCode+", "+httpMethod.getResponseBodyAsString()); 064 } finally { 065 httpMethod.releaseConnection(); 066 } 067 } catch (HttpRemoteException e) { 068 throw e; 069 } catch (Exception e) { 070 throw new HttpRemoteException("Remoting problem: "+e, e); 071 } 072 } 073 074 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 075 try { 076 Invocation invocation = new Invocation(null, method, args, reference.getId()); 077 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 078 ObjectOutputStream oos = new ObjectOutputStream(new GZIPOutputStream(baos)) { 079 080 { 081 enableReplaceObject(true); 082 } 083 084 protected Object replaceObject(Object obj) throws IOException { 085 if (Proxy.isProxyClass(obj.getClass())) { 086 InvocationHandler ih = Proxy.getInvocationHandler(obj); 087 if (ih instanceof HttpRemoteInvocationHandler) { 088 return ((HttpRemoteInvocationHandler) ih).reference; 089 } 090 } 091 return super.replaceObject(obj); 092 } 093 }; 094 oos.writeObject(invocation); 095 oos.close(); 096 097 HttpClient httpClient = new HttpClient(connectionManager); 098 PostMethod httpMethod = new PostMethod(reference.getRemoteUrl()); 099 try { 100 httpMethod.setRequestEntity(new ByteArrayRequestEntity(baos.toByteArray())); 101 if (cookie!=null) { 102 httpMethod.setRequestHeader("Cookie", cookie); 103 } 104 int statusCode = httpClient.executeMethod(httpMethod); 105 if (statusCode == HttpStatus.SC_OK) { 106 ObjectInputStream ois = new ObjectInputStream(new GZIPInputStream(new ByteArrayInputStream(httpMethod.getResponseBody()))) { 107 108 { 109 enableResolveObject(true); 110 } 111 112 protected Object resolveObject(Object obj) throws IOException { 113 if (obj instanceof HttpReference) { 114 try { 115 return createProxy((HttpReference) obj); 116 } catch (ClassNotFoundException e) { 117 throw new IOException("Could not create proxy: "+e); 118 } 119 } 120 return super.resolveObject(obj); 121 } 122 }; 123 try { 124 // First boolean is a discriminator - true - success, false - exception 125 if (ois.readBoolean()) { 126 return ois.readObject(); 127 } else { 128 throw (Exception) ois.readObject(); 129 } 130 } finally { 131 ois.close(); 132 } 133 } 134 throw new HttpRemoteException("Remoting problem, HTTP error code: "+statusCode+", "+httpMethod.getResponseBodyAsString()); 135 } finally { 136 httpMethod.releaseConnection(); 137 } 138 } catch (HttpRemoteException e) { 139 throw e; 140 } catch (Exception e) { 141 throw new HttpRemoteException("Remoting problem: "+e, e); 142 } 143 } 144 145 /** 146 * Creates proxy from reference 147 * @param reference 148 * @return 149 * @throws ClassNotFoundException 150 */ 151 protected Object createProxy(final HttpReference reference) throws ClassNotFoundException { 152 final HttpRemoteInvocationHandler ih = new HttpRemoteInvocationHandler( 153 reference, 154 cookie, 155 connectionManager, 156 classLoader); 157 158 class RemoteClassLoader extends ClassLoader { 159 160 public RemoteClassLoader(ClassLoader parent) { 161 super(parent); 162 } 163 164 // TODO load resources from remote location, cache, define classes, cache. 165 } 166 167 RemoteClassLoader remoteClassLoader = new RemoteClassLoader(classLoader==null ? getClass().getClassLoader() : classLoader); 168 169 Class[] interfaces = new Class[reference.getInterfaces().length]; 170 for (int i=0; i<interfaces.length; ++i) { 171 interfaces[i] = remoteClassLoader.loadClass(reference.getInterfaces()[i]); 172 } 173 174 return Proxy.newProxyInstance(remoteClassLoader, interfaces, ih); 175 176 } 177 178 }