(no commit message)
[utils] / support / general / src / main / java / org / wamblee / general / LookupProxyFactory.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.general;
17
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Proxy;
22
23 import javax.naming.InitialContext;
24 import javax.naming.NamingException;
25
26 /**
27  * Proxy factory that can provide contextual references to objects retrieved
28  * through a lookup mechanism.
29  * 
30  * @param T
31  *            Interface to proxy.
32  * @author Erik Brakkee
33  * 
34  */
35 public class LookupProxyFactory<T> {
36     
37     /**
38      * Interface to lookup the object to delegate to. 
39      * 
40      * @author Erik Brakkee
41      */
42     public static interface Lookup { 
43          /**
44           * Looks up the object. 
45           * @return Object (non-null)
46           * @throws Any exception in case the object cannot be found. 
47           */
48          Object lookup() throws Exception; 
49     }
50     
51     /**
52      * Exception thrown in case an object cannot be retrieved from JNDI. 
53      * 
54      * @author Erik Brakkee
55      */
56     public static class LookupException extends RuntimeException {
57         public LookupException(String aMsg, Throwable aCause) {
58             super(aMsg, aCause);
59         }
60
61         public LookupException(String aMsg) {
62             super(aMsg);
63         }
64     }
65
66     /**
67      * Invocation handler that does a lookup in JNDI and invokes the method on the 
68      * object it found. 
69      * 
70      * @author Erik Brakkee
71      */
72     private class LookupInvocationHandler implements InvocationHandler {
73
74         @Override
75         /**
76          * @throws JndiWiringException in case the object could not be retrieved from JNDI. 
77          */
78         public Object invoke(Object aProxy, Method aMethod, Object[] aArgs)
79             throws Throwable {
80             Object svcObj = null;
81             try {
82                 svcObj = lookup.lookup();
83             } catch (Exception e) {
84                 throw new LookupException(
85                     "Error looking up object", e);
86             }
87             if (svcObj == null) {
88                 throw new LookupException("Object at is null");
89             }
90             if (!clazz.isInstance(svcObj)) {
91                 throw new LookupException("Object '" + svcObj + "' is not of type " + clazz.getName() +
92                     " but of type " + svcObj.getClass().getName());
93             }
94             T svc = (T) svcObj;
95             try {
96                 return aMethod.invoke(svc, aArgs);
97             } catch (InvocationTargetException e) {
98                 throw e.getCause();
99             }
100         }
101     }
102
103     private Lookup lookup;
104     private Class clazz;
105
106     /**
107      * Constructs the factory.
108      * 
109      * @param aClass
110      *            Interface class of the service to proxy.
111      * @param aJndi JNDI name of the object to lookup. 
112      * 
113      */
114     public LookupProxyFactory(Class<T> aClass, Lookup aLookup) {
115         if (!aClass.isInterface()) {
116             throw new IllegalArgumentException("Class " + aClass.getName() +
117                 " is not an interface");
118         }
119         clazz = aClass;
120         lookup = aLookup; 
121     }
122
123     /**
124      * Gets the proxy that delegates to the thread-specific instance set by
125      * {@link #set(Object)}
126      * 
127      * When at runtime the proxy cannot find lookup the object in JNDI, it 
128      * throws {@link LookupException}. 
129      * 
130      * @return Proxy.
131      */
132     public T getProxy() {
133         InvocationHandler handler = new LookupInvocationHandler();
134         Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(),
135             new Class[] { clazz });
136         T proxy;
137         try {
138             proxy = (T) proxyClass.getConstructor(
139                 new Class[] { InvocationHandler.class }).newInstance(
140                 new Object[] { handler });
141             return proxy;
142         } catch (Exception e) {
143             throw new RuntimeException("Could not create proxy for " +
144                 clazz.getName(), e);
145         }
146     }
147 }