(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.io.Serializable;
19 import java.lang.reflect.InvocationHandler;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Proxy;
23
24 import javax.naming.InitialContext;
25 import javax.naming.NamingException;
26
27 /**
28  * Proxy factory that can provide contextual references to objects retrieved
29  * through a lookup mechanism. The returned proxies are serializable.
30  * 
31  * @param T
32  *            Interface to proxy.
33  * @author Erik Brakkee
34  * 
35  */
36 public class LookupProxyFactory<T> {
37     
38     /**
39      * Interface to lookup the object to delegate to. 
40      * 
41      * @author Erik Brakkee
42      */
43     public static interface Lookup extends Serializable { 
44          /**
45           * Looks up the object. 
46           * @return Object (non-null)
47           * @throws Any exception in case the object cannot be found. 
48           */
49          Object lookup() throws Exception; 
50     }
51     
52     /**
53      * Exception thrown in case an object cannot be retrieved from JNDI. 
54      * 
55      * @author Erik Brakkee
56      */
57     public static class LookupException extends RuntimeException {
58         public LookupException(String aMsg, Throwable aCause) {
59             super(aMsg, aCause);
60         }
61
62         public LookupException(String aMsg) {
63             super(aMsg);
64         }
65     }
66
67     /**
68      * Invocation handler that does a lookup in JNDI and invokes the method on the 
69      * object it found. 
70      * 
71      * @author Erik Brakkee
72      */
73     private static class LookupInvocationHandler<T> implements InvocationHandler, Serializable {
74         
75         private Class clazz; 
76         private Lookup lookup; 
77         
78         /**
79          * Constructs the invocation handler. 
80          * @param aLookup Lookup class. 
81          */
82         public LookupInvocationHandler(Class aClass, Lookup aLookup) {
83             clazz = aClass;
84             lookup = aLookup; 
85         }
86
87         @Override
88         /**
89          * @throws JndiWiringException in case the object could not be retrieved from JNDI. 
90          */
91         public Object invoke(Object aProxy, Method aMethod, Object[] aArgs)
92             throws Throwable {
93             Object svcObj = null;
94             try {
95                 svcObj = lookup.lookup();
96             } catch (Exception e) {
97                 throw new LookupException(
98                     "Error looking up object", e);
99             }
100             if (svcObj == null) {
101                 throw new LookupException("Object is null");
102             }
103             if (!clazz.isInstance(svcObj)) {
104                 throw new LookupException("Object '" + svcObj + "' is not of type " + clazz.getName() +
105                     " but of type " + svcObj.getClass().getName());
106             }
107             T svc = (T) svcObj;
108             try {
109                 return aMethod.invoke(svc, aArgs);
110             } catch (InvocationTargetException e) {
111                 throw e.getCause();
112             }
113         }
114     }
115
116     private Lookup lookup;
117     private Class clazz;
118
119     /**
120      * Constructs the factory.
121      * 
122      * @param aClass
123      *            Interface class of the service to proxy.
124      * @param aJndi JNDI name of the object to lookup. 
125      * 
126      */
127     public LookupProxyFactory(Class<T> aClass, Lookup aLookup) {
128         if (!aClass.isInterface()) {
129             throw new IllegalArgumentException("Class " + aClass.getName() +
130                 " is not an interface");
131         }
132         clazz = aClass;
133         lookup = aLookup; 
134     }
135
136     /**
137      * Gets the proxy that delegates to the thread-specific instance set by
138      * {@link #set(Object)}
139      * 
140      * When at runtime the proxy cannot find lookup the object in JNDI, it 
141      * throws {@link LookupException}. 
142      * 
143      * @return Proxy.
144      */
145     public T getProxy() {
146         InvocationHandler handler = new LookupInvocationHandler(clazz, lookup);
147         Class proxyClass = Proxy.getProxyClass(clazz.getClassLoader(),
148             new Class[] { clazz });
149         T proxy;
150         try {
151             proxy = (T) proxyClass.getConstructor(
152                 new Class[] { InvocationHandler.class }).newInstance(
153                 new Object[] { handler });
154             return proxy;
155         } catch (Exception e) {
156             throw new RuntimeException("Could not create proxy for " +
157                 clazz.getName(), e);
158         }
159     }
160 }