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    }