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