001 package biz.hammurapi.jms.adapter; 002 003 import java.io.Serializable; 004 import java.lang.reflect.Constructor; 005 import java.lang.reflect.InvocationHandler; 006 import java.lang.reflect.Proxy; 007 008 import biz.hammurapi.config.Component; 009 import biz.hammurapi.config.ConfigurationException; 010 import biz.hammurapi.config.GenericContainer; 011 import biz.hammurapi.config.RuntimeConfigurationException; 012 import biz.hammurapi.config.Wrapper; 013 import biz.hammurapi.jms.adapter.definition.ProxyServiceDocument; 014 import biz.hammurapi.metrics.MeasurementCollector; 015 import biz.hammurapi.metrics.MeasurementConsumer; 016 017 public class ProxyService extends GenericContainer implements Wrapper { 018 019 private final class ProxyServiceInvocationHandler implements InvocationHandler { 020 private LocalDelegate delegate; 021 022 private ProxyServiceInvocationHandler(LocalDelegate delegate) { 023 this.delegate = delegate; 024 } 025 026 Object getState() { 027 return delegate == null ? null : delegate.getState(); 028 } 029 030 void setState(Object state) { 031 if (delegate!=null) { 032 delegate.setState(state); 033 } 034 } 035 036 public Object invoke(final Object proxy, final java.lang.reflect.Method method, final Object[] args) throws Throwable { 037 Method jmsMethod = (Method) get(signature(method)); 038 if (jmsMethod==null || jmsMethod.getWrapperException()==null) { 039 return delegate.invoke(proxy, method, args, jmsMethod); 040 } else { 041 try { 042 return delegate.invoke(proxy, method, args, jmsMethod); 043 } catch (Exception e) { 044 boolean isWrapperDeclared = false; 045 Class[] declaredExceptions = method.getExceptionTypes(); 046 for (int i=0; i<declaredExceptions.length; ++i) { 047 if (declaredExceptions[i].isInstance(e)) { 048 throw e; 049 } 050 if (declaredExceptions[i].isAssignableFrom(jmsMethod.getWrapperException())) { 051 isWrapperDeclared = true; 052 } 053 } 054 055 if (e instanceof RuntimeException) { 056 throw e; 057 } 058 059 if (!isWrapperDeclared) { 060 throw e; // Wrapper shall be in the list of declared exceptions, we don't do double-wrapping. 061 } 062 063 Constructor[] constructors = jmsMethod.getWrapperException().getConstructors(); 064 065 /* 066 * 0 - String, Throwable 067 * 1 - Throwable, String 068 * 2 - Throwable 069 * 3 - String 070 * 4 - Default 071 */ 072 Constructor[] candidates = new Constructor[5]; 073 for (int i=0; i<constructors.length; ++i) { 074 Class[] cpt = constructors[i].getParameterTypes(); 075 switch (cpt.length) { 076 case 0: 077 candidates[4] = constructors[i]; 078 break; 079 case 1: 080 if (String.class.equals(cpt[0])) { 081 candidates[3] = constructors[i]; 082 } else if (Throwable.class.equals(cpt[0])) { 083 candidates[2] = constructors[i]; 084 } 085 break; 086 case 2: 087 if (String.class.equals(cpt[0]) && Throwable.class.equals(cpt[1])) { 088 candidates[0] = constructors[i]; 089 } else if (Throwable.class.equals(cpt[0]) && String.class.equals(cpt[1])) { 090 candidates[1] = constructors[i]; 091 } 092 break; 093 } 094 } 095 096 if (JmsAdapter.isBlank(e.getMessage())) { 097 if (candidates[2]!=null) { 098 throw (Exception) candidates[2].newInstance(new Object[] {e}); 099 } 100 101 if (candidates[0]!=null) { 102 throw (Exception) candidates[0].newInstance(new Object[] {e.getMessage(), e}); 103 } 104 105 if (candidates[1]!=null) { 106 throw (Exception) candidates[1].newInstance(new Object[] {e, e.getMessage()}); 107 } 108 109 if (candidates[3]!=null) { 110 throw (Exception) candidates[3].newInstance(new Object[] {e.toString()}); 111 } 112 } else { 113 if (candidates[0]!=null) { 114 throw (Exception) candidates[0].newInstance(new Object[] {e.getMessage(), e}); 115 } 116 117 if (candidates[1]!=null) { 118 throw (Exception) candidates[1].newInstance(new Object[] {e, e.getMessage()}); 119 } 120 121 if (candidates[2]!=null) { 122 throw (Exception) candidates[2].newInstance(new Object[] {e}); 123 } 124 125 if (candidates[3]!=null) { 126 throw (Exception) candidates[3].newInstance(new Object[] {e.toString()}); 127 } 128 } 129 130 if (candidates[4]!=null) { 131 throw (Exception) candidates[4].newInstance(null); 132 } 133 134 throw e; 135 } 136 } 137 } 138 } 139 140 /** 141 * @param proxy 142 * @return Local delegate of the proxy if it is serializable 143 */ 144 public static Object getProxyState(Object proxy) { 145 if (proxy!=null && Proxy.isProxyClass(proxy.getClass())) { 146 InvocationHandler ih = Proxy.getInvocationHandler(proxy); 147 if (ih instanceof ProxyServiceInvocationHandler) { 148 Object ret = ((ProxyServiceInvocationHandler) ih).getState(); 149 if (ret instanceof Serializable) { 150 return ret; 151 } 152 } 153 } 154 return null; 155 } 156 157 /** 158 * @param proxy 159 * @return Local delegate of the proxy if it is serializable 160 */ 161 public static void setProxyState(Object proxy, Object state) { 162 if (proxy!=null && Proxy.isProxyClass(proxy.getClass())) { 163 InvocationHandler ih = Proxy.getInvocationHandler(proxy); 164 if (ih instanceof ProxyServiceInvocationHandler) { 165 ((ProxyServiceInvocationHandler) ih).setState(state); 166 } 167 } 168 } 169 170 private biz.hammurapi.jms.adapter.definition.ProxyService definition; 171 172 private Class[] interfaces; 173 174 private JmsService jmsService; 175 176 JmsService getJmsService() { 177 if (definition.getService()!=null) { 178 return jmsService; 179 } else if (definition.getServiceRef()!=null) { 180 return (JmsService) get(definition.getServiceRef()); 181 } 182 183 return null; 184 } 185 186 public ProxyService(biz.hammurapi.jms.adapter.definition.ProxyService definition) throws ConfigurationException { 187 this.definition = definition; 188 if (definition.getService()!=null) { 189 jmsService = new JmsService(definition.getService()); 190 addComponent("jms-service", jmsService); 191 } 192 193 interfaces = new Class[definition.getInterfaceArray().length]; 194 for (int i=0; i<interfaces.length; ++i) { 195 try { 196 interfaces[i] = Class.forName(definition.getInterfaceArray()[i]); 197 } catch (ClassNotFoundException e) { 198 throw new ConfigurationException("Interface not found: "+definition.getInterfaceArray()[i]+": "+e, e); 199 } 200 } 201 202 for (int i=0; i<definition.getMethodArray().length; ++i) { 203 biz.hammurapi.jms.adapter.definition.Method method = definition.getMethodArray(i); 204 StringBuffer methodSignature = new StringBuffer(method.getName()); 205 methodSignature.append("("); 206 for (int j=0; j<method.getParameterArray().length; ++j) { 207 if (j>0) { 208 methodSignature.append(","); 209 } 210 methodSignature.append(method.getParameterArray(j)); 211 } 212 methodSignature.append(")"); 213 addComponent(methodSignature.toString(), new Method(method)); 214 } 215 216 } 217 218 /** 219 * Creates new proxy instance. Local state is associated with 220 * the instance. 221 * @return New proxy instance. 222 */ 223 public Object createProxy() { 224 final LocalDelegate localDelegate; 225 if (definition.getLocalDelegate()==null) { 226 localDelegate = new MapLocalDelegate(); 227 } else { 228 try { 229 localDelegate = (LocalDelegate) JmsAdapter.instantiate(definition.getLocalDelegate()); 230 if (localDelegate instanceof Component) { 231 ((Component) localDelegate).setOwner(this); 232 ((Component) localDelegate).start(); 233 } 234 if (localDelegate instanceof MeasurementCollector) { 235 MeasurementConsumer cmc = new MeasurementConsumer() { 236 public void addMeasurement(String mName, double value, long time) { 237 MeasurementConsumer measurementConsumer = getMeasurementConsumer(); 238 if (measurementConsumer != null) { 239 measurementConsumer.addMeasurement("local-delegate."+mName, value, time==0 ? System.currentTimeMillis() : time); 240 } 241 } 242 }; 243 ((MeasurementCollector) localDelegate).setMeasurementConsumer(cmc); 244 } 245 } catch (ConfigurationException e) { 246 throw new RuntimeConfigurationException("Cannot create local delegate: "+e, e); 247 } 248 } 249 250 InvocationHandler ih = new ProxyServiceInvocationHandler(localDelegate); 251 252 return Proxy.newProxyInstance(getClass().getClassLoader(), interfaces, ih); 253 } 254 255 public static String signature(final java.lang.reflect.Method method) { 256 final StringBuffer methodSignature = new StringBuffer(method.getName()); 257 methodSignature.append("("); 258 Class[] parameterTypes=method.getParameterTypes(); 259 for (int i=0; i<parameterTypes.length; ++i) { 260 if (i>0) { 261 methodSignature.append(","); 262 } 263 methodSignature.append(parameterTypes[i].getName()); 264 } 265 methodSignature.append(")"); 266 return methodSignature.toString(); 267 } 268 269 /** 270 * Generates proxy service XML by introspecting class which name is passed in the first parameter. 271 * @param args 272 */ 273 public static final void main(String[] args) throws Exception { 274 System.out.println("Usage: java [<options>] "+ProxyService.class.getName()+" <interface name>"); 275 if (args.length!=1) { 276 System.out.println("Invalid number of parameters"); 277 } else { 278 Class iClass = Class.forName(args[0]); 279 if (iClass.isInterface()) { 280 ProxyServiceDocument doc = ProxyServiceDocument.Factory.newInstance(); 281 biz.hammurapi.jms.adapter.definition.ProxyService ps = doc.addNewProxyService(); 282 ps.addInterface(iClass.getName()); 283 284 java.lang.reflect.Method[] methods = iClass.getMethods(); 285 for (int i=0; i<methods.length; ++i) { 286 biz.hammurapi.jms.adapter.definition.Method method = ps.addNewMethod(); 287 method.setName(methods[i].getName()); 288 Class[] parameterTypes = methods[i].getParameterTypes(); 289 for (int j=0; j<parameterTypes.length; ++j) { 290 method.addParameter(parameterTypes[j].getName()); 291 } 292 } 293 System.out.println(doc); 294 } else { 295 System.out.println(args[0]+" is not an interface"); 296 } 297 } 298 } 299 300 /** 301 * Invokes createProxy() 302 * @return 303 */ 304 public Object getMaster() { 305 return createProxy(); 306 } 307 }