001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.test.rule;
016    
017    import com.liferay.portal.aspectj.WeavingClassLoader;
018    import com.liferay.portal.kernel.process.ClassPathUtil;
019    import com.liferay.portal.kernel.process.ProcessCallable;
020    import com.liferay.portal.kernel.process.ProcessException;
021    import com.liferay.portal.kernel.test.ReflectionTestUtil;
022    import com.liferay.portal.kernel.test.rule.NewEnvTestRule;
023    import com.liferay.portal.kernel.util.ArrayUtil;
024    import com.liferay.portal.kernel.util.MethodKey;
025    import com.liferay.portal.kernel.util.StringBundler;
026    import com.liferay.portal.kernel.util.StringPool;
027    import com.liferay.portal.kernel.util.StringUtil;
028    import com.liferay.util.SerializableUtil;
029    
030    import java.io.File;
031    import java.io.Serializable;
032    
033    import java.net.MalformedURLException;
034    
035    import java.util.List;
036    
037    import org.aspectj.lang.annotation.Aspect;
038    
039    import org.junit.runner.Description;
040    
041    /**
042     * @author Shuyang Zhou
043     */
044    public class AspectJNewEnvTestRule extends NewEnvTestRule {
045    
046            public static final AspectJNewEnvTestRule INSTANCE =
047                    new AspectJNewEnvTestRule();
048    
049            @Override
050            protected List<String> createArguments(Description description) {
051                    List<String> arguments = super.createArguments(description);
052    
053                    AdviseWith adviseWith = description.getAnnotation(AdviseWith.class);
054    
055                    if (adviseWith == null) {
056                            return arguments;
057                    }
058    
059                    Class<?>[] adviceClasses = adviseWith.adviceClasses();
060    
061                    if (ArrayUtil.isEmpty(adviceClasses)) {
062                            return arguments;
063                    }
064    
065                    StringBundler sb = new StringBundler(adviceClasses.length * 2 + 1);
066    
067                    sb.append("-DaspectClasses=");
068    
069                    for (Class<?> adviceClass : adviceClasses) {
070                            Aspect aspect = adviceClass.getAnnotation(Aspect.class);
071    
072                            if (aspect == null) {
073                                    throw new IllegalArgumentException(
074                                            "Class " + adviceClass.getName() + " is not an aspect");
075                            }
076    
077                            sb.append(adviceClass.getName());
078                            sb.append(StringPool.COMMA);
079                    }
080    
081                    sb.setIndex(sb.index() - 1);
082    
083                    arguments.add(sb.toString());
084    
085                    return arguments;
086            }
087    
088            @Override
089            protected ClassLoader createClassLoader(Description description) {
090                    AdviseWith adviseWith = description.getAnnotation(AdviseWith.class);
091    
092                    if (adviseWith == null) {
093                            return super.createClassLoader(description);
094                    }
095    
096                    Class<?>[] adviceClasses = adviseWith.adviceClasses();
097    
098                    if (ArrayUtil.isEmpty(adviceClasses)) {
099                            return super.createClassLoader(description);
100                    }
101    
102                    for (Class<?> adviceClass : adviceClasses) {
103                            Aspect aspect = adviceClass.getAnnotation(Aspect.class);
104    
105                            if (aspect == null) {
106                                    throw new IllegalArgumentException(
107                                            "Class " + adviceClass.getName() + " is not an aspect");
108                            }
109                    }
110    
111                    String className = description.getClassName();
112    
113                    File dumpDir = new File(
114                            System.getProperty("junit.aspectj.dump"),
115                            className.concat(StringPool.PERIOD).concat(
116                                    description.getMethodName()));
117    
118                    try {
119                            return new WeavingClassLoader(
120                                    ClassPathUtil.getClassPathURLs(CLASS_PATH), adviceClasses,
121                                    dumpDir);
122                    }
123                    catch (MalformedURLException murle) {
124                            throw new RuntimeException(murle);
125                    }
126            }
127    
128            @Override
129            protected ProcessCallable<Serializable> processProcessCallable(
130                    ProcessCallable<Serializable> processCallable, MethodKey methodKey) {
131    
132                    Class<?> clazz = methodKey.getDeclaringClass();
133    
134                    String className = clazz.getName();
135    
136                    File dumpDir = new File(
137                            System.getProperty("junit.aspectj.dump"),
138                            className.concat(StringPool.PERIOD).concat(
139                                    methodKey.getMethodName()));
140    
141                    return new SwitchClassLoaderProcessCallable(processCallable, dumpDir);
142            }
143    
144            private AspectJNewEnvTestRule() {
145            }
146    
147            private static class SwitchClassLoaderProcessCallable
148                    implements ProcessCallable<Serializable> {
149    
150                    public SwitchClassLoaderProcessCallable(
151                            ProcessCallable<Serializable> processCallable, File dumpDir) {
152    
153                            _dumpDir = dumpDir;
154    
155                            _encodedProcessCallable = SerializableUtil.serialize(
156                                    processCallable);
157                            _toString = processCallable.toString();
158                    }
159    
160                    @Override
161                    public Serializable call() throws ProcessException {
162                            attachProcess("Attached " + toString());
163    
164                            String[] aspectClassNames = StringUtil.split(
165                                    System.getProperty("aspectClasses"));
166    
167                            Class<?>[] aspectClasses = new Class<?>[aspectClassNames.length];
168    
169                            Thread currentThread = Thread.currentThread();
170    
171                            ClassLoader contextClassLoader =
172                                    currentThread.getContextClassLoader();
173    
174                            try {
175                                    for (int i = 0; i < aspectClassNames.length; i++) {
176                                            aspectClasses[i] = contextClassLoader.loadClass(
177                                                    aspectClassNames[i]);
178                                    }
179    
180                                    WeavingClassLoader weavingClassLoader = new WeavingClassLoader(
181                                            ClassPathUtil.getClassPathURLs(
182                                                    ClassPathUtil.getJVMClassPath(true)),
183                                            aspectClasses, _dumpDir);
184    
185                                    currentThread.setContextClassLoader(weavingClassLoader);
186    
187                                    return ReflectionTestUtil.invoke(
188                                            SerializableUtil.deserialize(
189                                                    _encodedProcessCallable, weavingClassLoader),
190                                            "call", new Class<?>[0]);
191                            }
192                            catch (Exception e) {
193                                    throw new ProcessException(e);
194                            }
195                            finally {
196                                    currentThread.setContextClassLoader(contextClassLoader);
197                            }
198                    }
199    
200                    @Override
201                    public String toString() {
202                            return _toString;
203                    }
204    
205                    private static final long serialVersionUID = 1L;
206    
207                    private final File _dumpDir;
208                    private final byte[] _encodedProcessCallable;
209                    private final String _toString;
210    
211            }
212    
213    }