001    /**
002     * Copyright (c) 2000-2012 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.kernel.test;
016    
017    import com.liferay.portal.kernel.process.ClassPathUtil;
018    import com.liferay.portal.kernel.process.ProcessCallable;
019    import com.liferay.portal.kernel.process.ProcessException;
020    import com.liferay.portal.kernel.process.ProcessExecutor;
021    import com.liferay.portal.kernel.util.MethodHandler;
022    import com.liferay.portal.kernel.util.MethodKey;
023    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
024    import com.liferay.portal.kernel.util.StringBundler;
025    import com.liferay.portal.kernel.util.StringPool;
026    
027    import java.io.Serializable;
028    
029    import java.lang.reflect.InvocationTargetException;
030    
031    import java.util.ArrayList;
032    import java.util.List;
033    import java.util.concurrent.ExecutionException;
034    import java.util.concurrent.Future;
035    
036    import org.junit.After;
037    import org.junit.Before;
038    import org.junit.runners.BlockJUnit4ClassRunner;
039    import org.junit.runners.model.FrameworkMethod;
040    import org.junit.runners.model.InitializationError;
041    import org.junit.runners.model.Statement;
042    import org.junit.runners.model.TestClass;
043    
044    /**
045     * @author Shuyang Zhou
046     */
047    public class NewJVMJUnitTestRunner extends BlockJUnit4ClassRunner {
048    
049            public NewJVMJUnitTestRunner(Class<?> clazz) throws InitializationError {
050                    super(clazz);
051    
052                    _classPath = ClassPathUtil.getJVMClassPath(false);
053            }
054    
055            protected static void attachProcess(String message) {
056                    if (!Boolean.getBoolean("attached")) {
057                            ProcessExecutor.ProcessContext.attach(
058                                    message, 1000,
059                                    new ProcessExecutor.ShutdownHook() {
060    
061                                            public boolean shutdown(
062                                                    int shutdownCode, Throwable shutdownThrowable) {
063    
064                                                    System.exit(shutdownCode);
065    
066                                                    return true;
067                                            }
068    
069                                    });
070    
071                            System.setProperty("attached", StringPool.TRUE);
072                    }
073            }
074    
075            protected List<String> createArguments(FrameworkMethod frameworkMethod) {
076                    List<String> arguments = new ArrayList<String>();
077    
078                    boolean junitDebug = Boolean.getBoolean("junit.debug");
079    
080                    if (junitDebug) {
081                            arguments.add(_JPDA_OPTIONS);
082                    }
083    
084                    String fileName = System.getProperty(
085                            "net.sourceforge.cobertura.datafile");
086    
087                    if (fileName != null) {
088                            arguments.add("-Dnet.sourceforge.cobertura.datafile=" + fileName);
089                    }
090    
091                    return arguments;
092            }
093    
094            @Override
095            protected Statement methodBlock(FrameworkMethod frameworkMethod) {
096                    Thread currentThread = Thread.currentThread();
097    
098                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
099    
100                    PortalClassLoaderUtil.setClassLoader(contextClassLoader);
101    
102                    TestClass testClass = getTestClass();
103    
104                    List<FrameworkMethod> beforeFrameworkMethods =
105                            testClass.getAnnotatedMethods(Before.class);
106    
107                    List<FrameworkMethod> afterFrameworkMethods =
108                            testClass.getAnnotatedMethods(After.class);
109    
110                    List<String> arguments = createArguments(frameworkMethod);
111    
112                    Class<?> clazz = testClass.getJavaClass();
113    
114                    return new RunInNewJVMStatment(
115                            _classPath, arguments, clazz, beforeFrameworkMethods,
116                            frameworkMethod, afterFrameworkMethods);
117            }
118    
119            protected ProcessCallable<Serializable> processProcessCallable(
120                    ProcessCallable<Serializable> processCallable,
121                    MethodKey testMethodKey) {
122    
123                    return processCallable;
124            }
125    
126            private static final String _JPDA_OPTIONS =
127                    "-agentlib:jdwp=transport=dt_socket,address=8001,server=y,suspend=y";
128    
129            private String _classPath;
130    
131            private static class TestProcessCallable
132                    implements ProcessCallable<Serializable> {
133    
134                    public TestProcessCallable(
135                            String testClassName, List<MethodKey> beforeMethodKeys,
136                            MethodKey testMethodKey, List<MethodKey> afterMethodKeys) {
137    
138                            _testClassName = testClassName;
139                            _beforeMethodKeys = beforeMethodKeys;
140                            _testMethodKey = testMethodKey;
141                            _afterMethodKeys = afterMethodKeys;
142                    }
143    
144                    public Serializable call() throws ProcessException {
145                            attachProcess("Attached " + toString());
146    
147                            Thread currentThread = Thread.currentThread();
148    
149                            ClassLoader contextClassLoader =
150                                    currentThread.getContextClassLoader();
151    
152                            try {
153                                    Class<?> clazz = contextClassLoader.loadClass(_testClassName);
154    
155                                    Object object = clazz.newInstance();
156    
157                                    for (MethodKey beforeMethodKey : _beforeMethodKeys) {
158                                            _invoke(beforeMethodKey, object);
159                                    }
160    
161                                    _invoke(_testMethodKey, object);
162    
163                                    for (MethodKey afterMethodKey : _afterMethodKeys) {
164                                            _invoke(afterMethodKey, object);
165                                    }
166                            }
167                            catch (Exception e) {
168                                    throw new ProcessException(e);
169                            }
170    
171                            return StringPool.BLANK;
172                    }
173    
174                    @Override
175                    public String toString() {
176                            StringBundler sb = new StringBundler(4);
177    
178                            sb.append(_testClassName);
179                            sb.append(StringPool.PERIOD);
180                            sb.append(_testMethodKey.getMethodName());
181                            sb.append("()");
182    
183                            return sb.toString();
184                    }
185    
186                    private void _invoke(MethodKey methodKey, Object object)
187                            throws Exception {
188    
189                            MethodHandler methodHandler = new MethodHandler(methodKey);
190    
191                            methodHandler.invoke(object);
192                    }
193    
194                    private List<MethodKey> _afterMethodKeys;
195                    private List<MethodKey> _beforeMethodKeys;
196                    private String _testClassName;
197                    private MethodKey _testMethodKey;
198    
199            }
200    
201            private class RunInNewJVMStatment extends Statement {
202    
203                    public RunInNewJVMStatment(
204                            String classPath, List<String> arguments, Class<?> testClass,
205                            List<FrameworkMethod> beforeFrameworkMethods,
206                            FrameworkMethod testFrameworkMethod,
207                            List<FrameworkMethod> afterFrameworkMethods) {
208    
209                            _classPath = classPath;
210                            _arguments = arguments;
211                            _testClassName = testClass.getName();
212    
213                            _beforeMethodKeys = new ArrayList<MethodKey>(
214                                    beforeFrameworkMethods.size());
215    
216                            for (FrameworkMethod frameworkMethod : beforeFrameworkMethods) {
217                                    _beforeMethodKeys.add(
218                                            new MethodKey(frameworkMethod.getMethod()));
219                            }
220    
221                            _testMethodKey = new MethodKey(testFrameworkMethod.getMethod());
222    
223                            _afterMethodKeys = new ArrayList<MethodKey>(
224                                    afterFrameworkMethods.size());
225    
226                            for (FrameworkMethod frameworkMethod : afterFrameworkMethods) {
227                                    _afterMethodKeys.add(
228                                            new MethodKey(frameworkMethod.getMethod()));
229                            }
230                    }
231    
232                    @Override
233                    public void evaluate() throws Throwable {
234                            ProcessCallable<Serializable> processCallable =
235                                    new TestProcessCallable(
236                                            _testClassName, _beforeMethodKeys, _testMethodKey,
237                                            _afterMethodKeys);
238    
239                            processCallable = processProcessCallable(
240                                    processCallable, _testMethodKey);
241    
242                            Future<String> future = ProcessExecutor.execute(
243                                    _classPath, _arguments, processCallable);
244    
245                            try {
246                                    future.get();
247                            }
248                            catch (ExecutionException ee) {
249                                    Throwable cause = ee.getCause();
250    
251                                    while ((cause instanceof ProcessException) ||
252                                            (cause instanceof InvocationTargetException)) {
253    
254                                            cause = cause.getCause();
255                                    }
256    
257                                    throw cause;
258                            }
259                    }
260    
261                    private List<MethodKey> _afterMethodKeys;
262                    private List<String> _arguments;
263                    private List<MethodKey> _beforeMethodKeys;
264                    private String _classPath;
265                    private String _testClassName;
266                    private MethodKey _testMethodKey;
267    
268            }
269    
270    }