]> git.somenet.org - pub/jan/dst18.git/blob - ass2-aop/src/test/java/dst/ass2/aop/util/PluginUtils.java
[2.3.1] working plugin finder and executor.
[pub/jan/dst18.git] / ass2-aop / src / test / java / dst / ass2 / aop / util / PluginUtils.java
1 package dst.ass2.aop.util;
2
3 import static org.apache.commons.io.filefilter.FileFileFilter.FILE;
4 import static org.apache.commons.io.filefilter.FileFilterUtils.and;
5 import static org.apache.commons.io.filefilter.FileFilterUtils.or;
6 import static org.apache.commons.io.filefilter.FileFilterUtils.prefixFileFilter;
7 import static org.springframework.util.ReflectionUtils.findField;
8 import static org.springframework.util.ReflectionUtils.findMethod;
9 import static org.springframework.util.ReflectionUtils.getField;
10 import static org.springframework.util.ReflectionUtils.makeAccessible;
11
12 import java.io.File;
13 import java.io.FileFilter;
14 import java.io.IOException;
15 import java.lang.annotation.Annotation;
16 import java.lang.reflect.Field;
17 import java.lang.reflect.Method;
18 import java.lang.reflect.Modifier;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.UUID;
22 import java.util.logging.Handler;
23 import java.util.logging.Logger;
24
25 import dst.ass2.aop.event.EventBusHandler;
26 import org.apache.commons.io.FileUtils;
27 import org.aspectj.weaver.internal.tools.PointcutExpressionImpl;
28 import org.aspectj.weaver.patterns.Pointcut;
29 import org.springframework.aop.Advisor;
30 import org.springframework.aop.PointcutAdvisor;
31 import org.springframework.aop.aspectj.AbstractAspectJAdvice;
32 import org.springframework.aop.aspectj.AspectJExpressionPointcut;
33 import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
34 import org.springframework.aop.framework.Advised;
35 import org.springframework.aop.support.AopUtils;
36 import org.springframework.beans.BeanUtils;
37 import org.springframework.core.annotation.AnnotationUtils;
38 import org.springframework.core.io.ClassPathResource;
39
40 import dst.ass2.aop.IPluginExecutable;
41
42 /**
43  * Contains some utility methods for plugins.
44  */
45 public final class PluginUtils {
46
47     public final static int PLUGIN_TEST_TIMEOUT = 30000;
48
49     public static final File PLUGINS_DIR = new File(
50             FileUtils.getTempDirectoryPath(), "plugins_"
51             + System.currentTimeMillis());
52
53     public static File ALL_FILE;
54     public static File SIMPLE_FILE;
55
56     public static final Method EXECUTE_METHOD = findMethod(
57             IPluginExecutable.class, "execute");
58     public static final Method INTERRUPTED_METHOD = findMethod(
59             IPluginExecutable.class, "interrupted");
60
61     static {
62         try {
63             ALL_FILE = new ClassPathResource("all.zip").getFile();
64             SIMPLE_FILE = new ClassPathResource("simple.zip").getFile();
65         } catch (IOException e) {
66             throw new RuntimeException("Cannot locate plugin in classpath", e);
67         }
68     }
69
70     private PluginUtils() {
71     }
72
73     /**
74      * Modifies the value of a static (final) field and returns the previous
75      * value.
76      *
77      * @param clazz the class containing the static field
78      * @param name the name of the field
79      * @param value the value to set
80      * @return the previous value
81      * @throws IllegalAccessException if the field is inaccessible
82      */
83     @SuppressWarnings("unchecked")
84     public static <T> T setStaticFinalField(Class<?> clazz, String name, T value)
85             throws IllegalAccessException {
86         // Retrieve the desired field
87         Field field = findField(clazz, name);
88
89         // Remove the final modifier (if necessary)
90         if (Modifier.isFinal(field.getModifiers())) {
91             Field modifiers = findField(field.getClass(), "modifiers");
92             makeAccessible(modifiers);
93             modifiers.set(field, (Integer) modifiers.get(field)
94                     & ~Modifier.FINAL);
95         }
96
97         // Get the current value
98         T current = (T) field.get(null);
99
100         // Set the new value
101         field.set(null, value);
102
103         return current;
104     }
105
106     /**
107      * Creates a new unique {@link File} object within the {@link #PLUGINS_DIR}
108      * directory.
109      *
110      * @return the file
111      */
112     public static File uniqueFile() {
113         return new File(PLUGINS_DIR, "_" + System.nanoTime() + ".jar");
114     }
115
116     /**
117      * Copies the given file to a file in the plugin directory.<br/>
118      *
119      * @throws IOException if the destination file already exists or the file was not copied
120      * @see #uniqueFile()
121      */
122     public static void preparePlugin(File file) throws IOException {
123         File destFile = uniqueFile();
124         if (destFile.exists()) {
125             throw new IOException("Destination file must not exist.");
126         }
127
128         File tempFile = new File(destFile.getParentFile(), "tmp_"
129                 + UUID.randomUUID().toString());
130         FileUtils.copyFile(file, tempFile);
131         if (!tempFile.renameTo(destFile) || !destFile.isFile()) {
132             throw new IOException(String.format(
133                     "File '%s' was not copied to '%s'.", file, destFile));
134         }
135     }
136
137     /**
138      * Deletes all plugin JARs copied to the plugin directory.
139      */
140     public static void cleanPluginDirectory() {
141         FileFilter filter = and(FILE,
142                 or(prefixFileFilter("_"), prefixFileFilter("tmp_")));
143         System.gc();
144
145         for (File file : PLUGINS_DIR.listFiles(filter)) {
146             file.delete();
147         }
148     }
149
150     /**
151      * Ads a new {@link EventBusHandler} to the logger declared within the given
152      * objects class if necessary.<br/>
153      * This method does nothing if the logger already has an
154      * {@link EventBusHandler} or there is no logger.
155      *
156      * @param obj the object
157      */
158     public static void addBusHandlerIfNecessary(Object obj) {
159         Class<?> targetClass = AopUtils.getTargetClass(obj);
160         Field field = findField(targetClass, null, Logger.class);
161         if (field != null) {
162             makeAccessible(field);
163             Logger logger = (Logger) getField(field, obj);
164             for (Handler handler : logger.getHandlers()) {
165                 if (handler instanceof EventBusHandler) {
166                     return;
167                 }
168             }
169             logger.addHandler(new EventBusHandler());
170         }
171     }
172
173     /**
174      * Creates a new instance of the given {@link IPluginExecutable} class and
175      * returns a proxy with the AspectJ aspect applied to it.<br/>
176      * If {@code aspectClass} is {@code null}, no aspect is applied.
177      *
178      * @param clazz the plugin class
179      * @param aspectClass the class containing AspectJ definitions
180      * @return proxy of the plugin instance
181      */
182     public static IPluginExecutable getExecutable(
183             Class<? extends IPluginExecutable> clazz, Class<?> aspectClass) {
184         IPluginExecutable target = BeanUtils.instantiateClass(clazz);
185         AspectJProxyFactory factory = new AspectJProxyFactory(target);
186         factory.setProxyTargetClass(true);
187         if (aspectClass != null) {
188             factory.addAspect(BeanUtils.instantiateClass(aspectClass));
189         }
190         return factory.getProxy();
191     }
192
193     /**
194      * Returns the pointcut expression of the given advised proxy.
195      *
196      * @param advised the proxy with the applied aspect
197      * @return the pointcut expression or {@code null} if none was found
198      */
199     public static PointcutExpressionImpl getPointcutExpression(Advised advised) {
200         PointcutAdvisor pointcutAdvisor = getPointcutAdvisor(advised);
201         if (pointcutAdvisor != null) {
202             AspectJExpressionPointcut pointcut = (AspectJExpressionPointcut) pointcutAdvisor
203                     .getPointcut();
204             if (pointcut.getPointcutExpression() instanceof PointcutExpressionImpl) {
205                 return (PointcutExpressionImpl) pointcut
206                         .getPointcutExpression();
207             }
208         }
209         return null;
210     }
211
212     /**
213      * Returns the pointcut advisor of the given proxy if its advice part is an
214      * {@link AbstractAspectJAdvice}.
215      *
216      * @param advised the proxy with the applied aspect
217      * @return the pointcut advisor or {@code null} if there is no AspectJ pointcut advisor applied
218      */
219     public static PointcutAdvisor getPointcutAdvisor(Advised advised) {
220         for (Advisor advisor : advised.getAdvisors()) {
221             if (advisor instanceof PointcutAdvisor
222                     && advisor.getAdvice() instanceof AbstractAspectJAdvice) {
223                 return (PointcutAdvisor) advisor;
224             }
225         }
226         return null;
227     }
228
229     /**
230      * Attempts to resolve all parts of the pointcut expression of the aspect
231      * applied to the given proxy.
232      *
233      * @param advised the proxy with the applied aspect
234      * @return a string representation of this pointcut expression
235      * @see #getPointcutExpression(org.springframework.aop.framework.Advised)
236      * @see #getPointcutAdvisor(org.springframework.aop.framework.Advised)
237      */
238     public static String getBestExpression(Advised advised) {
239         PointcutExpressionImpl pointcutExpression = getPointcutExpression(advised);
240         if (pointcutExpression != null) {
241             Pointcut underlyingPointcut = pointcutExpression
242                     .getUnderlyingPointcut();
243             if (findMethod(underlyingPointcut.getClass(), "toString")
244                     .getDeclaringClass() != Object.class) {
245                 return underlyingPointcut.toString();
246             }
247             return pointcutExpression.getPointcutExpression();
248         }
249         PointcutAdvisor advisor = getPointcutAdvisor(advised);
250         AspectJExpressionPointcut pointcut = (AspectJExpressionPointcut) advisor
251                 .getPointcut();
252         return pointcut.getExpression();
253     }
254
255     /**
256      * Finds all public methods of the given class annotated with a certain
257      * annotation.
258      *
259      * @param clazz the class
260      * @param annotationType the annotation to search for
261      * @return methods annotated with the given annotation
262      */
263     public static <A extends Annotation> Map<Method, A> findMethodAnnotation(
264             Class<?> clazz, Class<A> annotationType) {
265         Map<Method, A> map = new HashMap<Method, A>();
266         for (Method method : clazz.getMethods()) {
267             A annotation = AnnotationUtils.findAnnotation(method,
268                     annotationType);
269             if (annotation != null) {
270                 map.put(method, annotation);
271             }
272         }
273         return map;
274     }
275 }