1 package dst.ass2.aop.util;
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;
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;
21 import java.util.UUID;
22 import java.util.logging.Handler;
23 import java.util.logging.Logger;
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;
40 import dst.ass2.aop.IPluginExecutable;
43 * Contains some utility methods for plugins.
45 public final class PluginUtils {
47 public final static int PLUGIN_TEST_TIMEOUT = 30000;
49 public static final File PLUGINS_DIR = new File(
50 FileUtils.getTempDirectoryPath(), "plugins_"
51 + System.currentTimeMillis());
53 public static File ALL_FILE;
54 public static File SIMPLE_FILE;
56 public static final Method EXECUTE_METHOD = findMethod(
57 IPluginExecutable.class, "execute");
58 public static final Method INTERRUPTED_METHOD = findMethod(
59 IPluginExecutable.class, "interrupted");
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);
70 private PluginUtils() {
74 * Modifies the value of a static (final) field and returns the previous
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
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);
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)
97 // Get the current value
98 T current = (T) field.get(null);
101 field.set(null, value);
107 * Creates a new unique {@link File} object within the {@link #PLUGINS_DIR}
112 public static File uniqueFile() {
113 return new File(PLUGINS_DIR, "_" + System.nanoTime() + ".jar");
117 * Copies the given file to a file in the plugin directory.<br/>
119 * @throws IOException if the destination file already exists or the file was not copied
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.");
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));
138 * Deletes all plugin JARs copied to the plugin directory.
140 public static void cleanPluginDirectory() {
141 FileFilter filter = and(FILE,
142 or(prefixFileFilter("_"), prefixFileFilter("tmp_")));
145 for (File file : PLUGINS_DIR.listFiles(filter)) {
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.
156 * @param obj the object
158 public static void addBusHandlerIfNecessary(Object obj) {
159 Class<?> targetClass = AopUtils.getTargetClass(obj);
160 Field field = findField(targetClass, null, Logger.class);
162 makeAccessible(field);
163 Logger logger = (Logger) getField(field, obj);
164 for (Handler handler : logger.getHandlers()) {
165 if (handler instanceof EventBusHandler) {
169 logger.addHandler(new EventBusHandler());
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.
178 * @param clazz the plugin class
179 * @param aspectClass the class containing AspectJ definitions
180 * @return proxy of the plugin instance
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));
190 return factory.getProxy();
194 * Returns the pointcut expression of the given advised proxy.
196 * @param advised the proxy with the applied aspect
197 * @return the pointcut expression or {@code null} if none was found
199 public static PointcutExpressionImpl getPointcutExpression(Advised advised) {
200 PointcutAdvisor pointcutAdvisor = getPointcutAdvisor(advised);
201 if (pointcutAdvisor != null) {
202 AspectJExpressionPointcut pointcut = (AspectJExpressionPointcut) pointcutAdvisor
204 if (pointcut.getPointcutExpression() instanceof PointcutExpressionImpl) {
205 return (PointcutExpressionImpl) pointcut
206 .getPointcutExpression();
213 * Returns the pointcut advisor of the given proxy if its advice part is an
214 * {@link AbstractAspectJAdvice}.
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
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;
230 * Attempts to resolve all parts of the pointcut expression of the aspect
231 * applied to the given proxy.
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)
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();
247 return pointcutExpression.getPointcutExpression();
249 PointcutAdvisor advisor = getPointcutAdvisor(advised);
250 AspectJExpressionPointcut pointcut = (AspectJExpressionPointcut) advisor
252 return pointcut.getExpression();
256 * Finds all public methods of the given class annotated with a certain
259 * @param clazz the class
260 * @param annotationType the annotation to search for
261 * @return methods annotated with the given annotation
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,
269 if (annotation != null) {
270 map.put(method, annotation);