001    /*
002     * Vestigo - The JDO/JPA Query Tool And Browser - http://vestigo.nightlabs.com
003     * Copyright © 2011-2012 NightLabs Consulting GmbH. All rights reserved.
004     *
005     * This program and all its libraries in the namespace "*.nightlabs.vestigo.*"
006     * are proprietary software. Their source codes are trade secrets and therefore
007     * must be kept confidential.
008     *
009     * The use of this software is subject to licence terms.
010     *
011     * Please see LICENCE.txt or
012     * http://vestigo.nightlabs.com/latest-stable/about/licence.html for
013     * more details.
014     *
015     * For further information, please contact NightLabs Consulting GmbH:
016     * http://nightlabs.com
017     */
018    package org.cumulus4j.store.reflectionwrapper;
019    
020    import java.lang.reflect.InvocationTargetException;
021    import java.lang.reflect.Method;
022    import java.util.Collections;
023    import java.util.HashMap;
024    import java.util.Map;
025    
026    public abstract class ReflectionWrapper
027    {
028            private ClassLoader classLoader;
029            private volatile Class<?> wrappedClass;
030            private volatile Object wrappedObject;
031    
032            public ReflectionWrapper(ClassLoader classLoader)
033            {
034                    if (classLoader == null)
035                            throw new IllegalArgumentException("classLoader == null");
036    
037                    this.classLoader = classLoader;
038            }
039    
040            public ReflectionWrapper(ReflectionWrapper persistenceEngineWrapper, Object wrappedObject) {
041                    if (persistenceEngineWrapper == null)
042                            throw new IllegalArgumentException("persistenceEngineWrapper == null");
043    
044                    this.classLoader = persistenceEngineWrapper.getClassLoader();
045                    this.wrappedObject = wrappedObject;
046                    if (wrappedObject != null)
047                            wrappedClass = wrappedObject.getClass();
048            }
049    
050            protected ClassLoader getClassLoader() {
051                    return classLoader;
052            }
053    
054            protected String getWrappedClassName()
055            {
056                    Class<?> clazz = wrappedClass;
057                    if (clazz != null)
058                            return clazz.getName();
059                    else
060                            throw new UnsupportedOperationException("Lazy creation not implemented! The wrappedObject should have been created eagerly or this method should have been overridden!");
061            }
062    
063            public Class<?> getWrappedClass()
064            {
065                    Class<?> clazz = wrappedClass;
066                    if (clazz == null) {
067                            try {
068                                    clazz = Class.forName(getWrappedClassName(), true, getClassLoader());
069                            } catch (ClassNotFoundException e) {
070                                    throw new ReflectionWrapperException(e);
071                            }
072                            wrappedClass = clazz;
073                    }
074                    return clazz;
075            }
076    
077            protected Object createWrappedObject(Class<?> wrappedClass)
078            {
079                    throw new UnsupportedOperationException("Lazy creation not implemented! The wrappedObject should have been created eagerly or this method should have been overridden!");
080            }
081    
082            public Object getWrappedObject()
083            {
084                    Object object = wrappedObject;
085                    if (object == null) {
086                            Class<?> wrappedClass = getWrappedClass();
087                            synchronized (this) {
088                                    object = wrappedObject;
089                                    if (object == null) {
090                                            object = createWrappedObject(wrappedClass);
091                                            wrappedObject = object;
092                                    }
093                            }
094                    }
095                    return object;
096            }
097    
098            private Map<Integer, Method> methodID2Method = Collections.synchronizedMap(new HashMap<Integer, Method>());
099    
100            protected Object invokeStatic(int methodID, String methodName)
101            {
102                    return invokeStatic(methodID, methodName, EMPTY_CLASS_ARRAY);
103            }
104    
105            protected Object invokeStatic(int methodID, String methodName, Class<?> parameterType, Object parameter)
106            {
107                    return invokeStatic(methodID, methodName, new Class<?>[] { parameterType }, parameter);
108            }
109    
110            protected Object invokeStatic(int methodID, String methodName, Class<?> parameterType1, Class<?> parameterType2, Object ...parameters)
111            {
112                    return invokeStatic(methodID, methodName, new Class<?>[] { parameterType1, parameterType2 }, parameters);
113            }
114    
115            protected Object invokeStatic(int methodID, String methodName, Class<?> parameterType1, Class<?> parameterType2, Class<?> parameterType3, Object ...parameters)
116            {
117                    return invokeStatic(methodID, methodName, new Class<?>[] { parameterType1, parameterType2, parameterType3 }, parameters);
118            }
119    
120            /**
121             * Invoke a method on the wrapped class (not the wrapped object) in a static way.
122             * @param methodID identifier that must be unique within the subclass of {@link ReflectionWrapper}.
123             * For performance reasons, the {@link Method} instances are cached and the cache key is this <code>methodID</code>
124             * (rather than a long String comprising <code>methodName</code> and <code>parameterTypes</code>).
125             * @param methodName method name as passed to {@link Class#getMethod(String, Class...)}.
126             * @param parameterTypes parameter types as passed to {@link Class#getMethod(String, Class...)}.
127             * @param parameters parameters as passed to {@link Method#invoke(Object, Object...)}.
128             * @return the result of the method invocation as returned from {@link Method#invoke(Object, Object...)}.
129             */
130            protected Object invokeStatic(int methodID, String methodName, Class<?>[] parameterTypes, Object ...parameters)
131            {
132                    try {
133                            Integer mID = methodID;
134                            Method method = methodID2Method.get(mID);
135                            if (method == null) {
136                                    method = getWrappedClass().getMethod(methodName, parameterTypes);
137                                    methodID2Method.put(mID, method);
138                            }
139    
140                            Object result = method.invoke(null, parameters);
141                            return result;
142                    } catch (SecurityException e) {
143                            throw new ReflectionWrapperException(e);
144                    } catch (NoSuchMethodException e) {
145                            throw new ReflectionWrapperException(e);
146                    } catch (IllegalArgumentException e) {
147                            throw new ReflectionWrapperException(e);
148                    } catch (IllegalAccessException e) {
149                            throw new ReflectionWrapperException(e);
150                    } catch (InvocationTargetException e) {
151                            throw new ReflectionWrapperException(e.getTargetException());
152                    }
153            }
154    
155            private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
156    
157            /**
158             * Invoke a method on the wrapped object. This is a convenience method delegating to
159             * {@link #invoke(int, String, Class[], Object...)}.
160             * @param methodID identifier that must be unique within the subclass of {@link ReflectionWrapper}.
161             * @param methodName method name.
162             * @return the result of the method invocation.
163             */
164            protected Object invoke(int methodID, String methodName)
165            {
166                    return invoke(methodID, methodName, EMPTY_CLASS_ARRAY);
167            }
168    
169            /**
170             * Invoke a method on the wrapped object. This is a convenience method delegating to
171             * {@link #invoke(int, String, Class[], Object...)}.
172             * @param methodID identifier that must be unique within the subclass of {@link ReflectionWrapper}.
173             * @param methodName method name.
174             * @param parameterType single parameter type.
175             * @param parameter single parameter.
176             * @return the result of the method invocation.
177             */
178            protected Object invoke(int methodID, String methodName, Class<?> parameterType, Object parameter)
179            {
180                    return invoke(methodID, methodName, new Class<?>[] { parameterType }, parameter);
181            }
182    
183            /**
184             * Invoke a method on the wrapped object. This is a convenience method delegating to
185             * {@link #invoke(int, String, Class[], Object...)}.
186             * @param methodID identifier that must be unique within the subclass of {@link ReflectionWrapper}.
187             * @param methodName method name.
188             * @param parameterType1 first parameter type.
189             * @param parameterType2 second parameter type.
190             * @param parameters two parameters (corresponding to the two parameter types).
191             * @return the result of the method invocation.
192             */
193            protected Object invoke(int methodID, String methodName, Class<?> parameterType1, Class<?> parameterType2, Object ...parameters)
194            {
195                    return invoke(methodID, methodName, new Class<?>[] { parameterType1, parameterType2 }, parameters);
196            }
197    
198            /**
199             * Invoke a method on the wrapped object. This is a convenience method delegating to
200             * {@link #invoke(int, String, Class[], Object...)}.
201             * @param methodID identifier that must be unique within the subclass of {@link ReflectionWrapper}.
202             * @param methodName method name.
203             * @param parameterType1 first parameter type.
204             * @param parameterType2 second parameter type.
205             * @param parameterType3 third parameter type.
206             * @param parameters three parameters (corresponding to the three parameter types).
207             * @return the result of the method invocation.
208             */
209            protected Object invoke(int methodID, String methodName, Class<?> parameterType1, Class<?> parameterType2, Class<?> parameterType3, Object ...parameters)
210            {
211                    return invoke(methodID, methodName, new Class<?>[] { parameterType1, parameterType2, parameterType3 }, parameters);
212            }
213    
214            /**
215             * Invoke a method on the wrapped object.
216             * @param methodID identifier that must be unique within the subclass of {@link ReflectionWrapper}.
217             * For performance reasons, the {@link Method} instances are cached and the cache key is this <code>methodID</code>
218             * (rather than a long String comprising <code>methodName</code> and <code>parameterTypes</code>).
219             * @param methodName method name as passed to {@link Class#getMethod(String, Class...)}.
220             * @param parameterTypes parameter types as passed to {@link Class#getMethod(String, Class...)}.
221             * @param parameters parameters as passed to {@link Method#invoke(Object, Object...)}.
222             * @return the result of the method invocation as returned from {@link Method#invoke(Object, Object...)}.
223             */
224            protected Object invoke(int methodID, String methodName, Class<?>[] parameterTypes, Object ...parameters)
225            {
226                    try {
227                            Integer mID = methodID;
228                            Method method = methodID2Method.get(mID);
229                            if (method == null) {
230                                    method = getWrappedClass().getMethod(methodName, parameterTypes);
231                                    methodID2Method.put(mID, method);
232                            }
233    
234                            Object result = method.invoke(getWrappedObject(), parameters);
235                            return result;
236                    } catch (SecurityException e) {
237                            throw new ReflectionWrapperException(e);
238                    } catch (NoSuchMethodException e) {
239                            throw new ReflectionWrapperException(e);
240                    } catch (IllegalArgumentException e) {
241                            throw new ReflectionWrapperException(e);
242                    } catch (IllegalAccessException e) {
243                            throw new ReflectionWrapperException(e);
244                    } catch (InvocationTargetException e) {
245                            throw new ReflectionWrapperException(e.getTargetException());
246                    }
247            }
248    }