001
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.ProcessChannel;
020 import com.liferay.portal.kernel.process.ProcessConfig;
021 import com.liferay.portal.kernel.process.ProcessConfig.Builder;
022 import com.liferay.portal.kernel.process.ProcessException;
023 import com.liferay.portal.kernel.process.ProcessExecutor;
024 import com.liferay.portal.kernel.process.local.LocalProcessExecutor;
025 import com.liferay.portal.kernel.process.local.LocalProcessLauncher.ProcessContext;
026 import com.liferay.portal.kernel.process.local.LocalProcessLauncher.ShutdownHook;
027 import com.liferay.portal.kernel.test.BaseTestRule.StatementWrapper;
028 import com.liferay.portal.kernel.util.MethodCache;
029 import com.liferay.portal.kernel.util.MethodKey;
030 import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
031 import com.liferay.portal.kernel.util.StringBundler;
032 import com.liferay.portal.kernel.util.StringPool;
033 import com.liferay.portal.kernel.util.SystemProperties;
034 import com.liferay.portal.kernel.util.Validator;
035
036 import java.io.Serializable;
037
038 import java.lang.annotation.Annotation;
039 import java.lang.reflect.InvocationTargetException;
040 import java.lang.reflect.Method;
041
042 import java.net.MalformedURLException;
043 import java.net.URLClassLoader;
044
045 import java.util.ArrayList;
046 import java.util.List;
047 import java.util.concurrent.ExecutionException;
048 import java.util.concurrent.Future;
049
050 import org.junit.After;
051 import org.junit.Before;
052 import org.junit.rules.TestRule;
053 import org.junit.runner.Description;
054 import org.junit.runners.model.FrameworkMethod;
055 import org.junit.runners.model.Statement;
056 import org.junit.runners.model.TestClass;
057
058
061 public class NewEnvTestRule implements TestRule {
062
063 public static final NewEnvTestRule INSTANCE = new NewEnvTestRule();
064
065 @Override
066 public Statement apply(Statement statement, Description description) {
067 String methodName = description.getMethodName();
068
069 if (methodName == null) {
070 return statement;
071 }
072
073 NewEnv newEnv = findNewEnv(description);
074
075 if ((newEnv == null) || (newEnv.type() == NewEnv.Type.NONE)) {
076 return statement;
077 }
078
079 if (NewEnv.Type.CLASSLOADER == newEnv.type()) {
080 return new RunInNewClassLoaderStatement(statement, description);
081 }
082
083 Builder builder = new Builder();
084
085 builder.setArguments(createArguments(description));
086 builder.setBootstrapClassPath(CLASS_PATH);
087 builder.setRuntimeClassPath(CLASS_PATH);
088
089 return new RunInNewJVMStatment(builder.build(), statement, description);
090 }
091
092 protected static void attachProcess(String message) {
093 if (Boolean.getBoolean("attached")) {
094 return;
095 }
096
097 ProcessContext.attach(
098 message, 1000,
099 new ShutdownHook() {
100
101 @Override
102 public boolean shutdown(
103 int shutdownCode, Throwable shutdownThrowable) {
104
105 System.exit(shutdownCode);
106
107 return true;
108 }
109
110 });
111
112 System.setProperty("attached", StringPool.TRUE);
113 }
114
115 protected static List<MethodKey> getMethodKeys(
116 Class<?> targetClass, Class<? extends Annotation> annotationClass) {
117
118 TestClass testClass = new TestClass(targetClass);
119
120 List<FrameworkMethod> frameworkMethods = testClass.getAnnotatedMethods(
121 annotationClass);
122
123 List<MethodKey> methodKeys = new ArrayList<MethodKey>(
124 frameworkMethods.size());
125
126 for (FrameworkMethod annotatedFrameworkMethod : frameworkMethods) {
127 methodKeys.add(new MethodKey(annotatedFrameworkMethod.getMethod()));
128 }
129
130 return methodKeys;
131 }
132
133 protected static void invoke(
134 ClassLoader classLoader, MethodKey methodKey, Object object)
135 throws Exception {
136
137 methodKey = methodKey.transform(classLoader);
138
139 Method method = methodKey.getMethod();
140
141 method.invoke(object);
142 }
143
144 protected NewEnvTestRule() {
145 }
146
147 protected List<String> createArguments(Description description) {
148 List<String> arguments = new ArrayList<String>();
149
150 if (Boolean.getBoolean("junit.debug")) {
151 arguments.add(_JPDA_OPTIONS);
152 arguments.add("-Djunit.debug=true");
153 }
154
155 String agentLine = System.getProperty("junit.cobertura.agent");
156
157 if (Validator.isNotNull(agentLine)) {
158 arguments.add(agentLine);
159 arguments.add("-Djunit.cobertura.agent=" + agentLine);
160 }
161
162 if (Boolean.getBoolean("junit.code.coverage")) {
163 arguments.add("-Djunit.code.coverage=true");
164 }
165
166 if (Boolean.getBoolean("junit.code.coverage.dump")) {
167 arguments.add("-Djunit.code.coverage.dump=true");
168 }
169
170 arguments.add("-Djava.net.preferIPv4Stack=true");
171
172 String fileName = System.getProperty(
173 "net.sourceforge.cobertura.datafile");
174
175 if (fileName != null) {
176 arguments.add("-Dnet.sourceforge.cobertura.datafile=" + fileName);
177 }
178
179 return arguments;
180 }
181
182 protected ClassLoader createClassLoader(Description description) {
183 try {
184 return new URLClassLoader(
185 ClassPathUtil.getClassPathURLs(CLASS_PATH), null);
186 }
187 catch (MalformedURLException murle) {
188 throw new RuntimeException(murle);
189 }
190 }
191
192 protected NewEnv findNewEnv(Description description) {
193 NewEnv newEnv = description.getAnnotation(NewEnv.class);
194
195 if (newEnv == null) {
196 Class<?> testClass = description.getTestClass();
197
198 newEnv = testClass.getAnnotation(NewEnv.class);
199 }
200
201 return newEnv;
202 }
203
204 protected ProcessCallable<Serializable> processProcessCallable(
205 ProcessCallable<Serializable> processCallable,
206 MethodKey testMethodKey) {
207
208 return processCallable;
209 }
210
211 protected static final String CLASS_PATH = ClassPathUtil.getJVMClassPath(
212 true);
213
214 private static final String _JPDA_OPTIONS =
215 "-agentlib:jdwp=transport=dt_socket,address=8001,server=y,suspend=y";
216
217 private static final ProcessExecutor _processExecutor =
218 new LocalProcessExecutor();
219
220 static {
221 Thread currentThread = Thread.currentThread();
222
223 ClassLoader contextClassLoader = currentThread.getContextClassLoader();
224
225 PortalClassLoaderUtil.setClassLoader(contextClassLoader);
226 }
227
228 private static class TestProcessCallable
229 implements ProcessCallable<Serializable> {
230
231 public TestProcessCallable(
232 String testClassName, List<MethodKey> beforeMethodKeys,
233 MethodKey testMethodKey, List<MethodKey> afterMethodKeys) {
234
235 _testClassName = testClassName;
236 _beforeMethodKeys = beforeMethodKeys;
237 _testMethodKey = testMethodKey;
238 _afterMethodKeys = afterMethodKeys;
239 }
240
241 @Override
242 public Serializable call() throws ProcessException {
243 attachProcess("Attached " + toString());
244
245 Thread currentThread = Thread.currentThread();
246
247 ClassLoader contextClassLoader =
248 currentThread.getContextClassLoader();
249
250 System.setProperty(
251 SystemProperties.SYSTEM_PROPERTIES_QUIET, StringPool.TRUE);
252
253 try {
254 Class<?> clazz = contextClassLoader.loadClass(_testClassName);
255
256 Object object = clazz.newInstance();
257
258 for (MethodKey beforeMethodKey : _beforeMethodKeys) {
259 invoke(contextClassLoader, beforeMethodKey, object);
260 }
261
262 invoke(contextClassLoader, _testMethodKey, object);
263
264 for (MethodKey afterMethodKey : _afterMethodKeys) {
265 invoke(contextClassLoader, afterMethodKey, object);
266 }
267 }
268 catch (Exception e) {
269 throw new ProcessException(e);
270 }
271
272 return StringPool.BLANK;
273 }
274
275 @Override
276 public String toString() {
277 StringBundler sb = new StringBundler(4);
278
279 sb.append(_testClassName);
280 sb.append(StringPool.PERIOD);
281 sb.append(_testMethodKey.getMethodName());
282 sb.append("()");
283
284 return sb.toString();
285 }
286
287 private static final long serialVersionUID = 1L;
288
289 private final List<MethodKey> _afterMethodKeys;
290 private final List<MethodKey> _beforeMethodKeys;
291 private final String _testClassName;
292 private final MethodKey _testMethodKey;
293
294 }
295
296 private class RunInNewClassLoaderStatement extends StatementWrapper {
297
298 public RunInNewClassLoaderStatement(
299 Statement statement, Description description) {
300
301 super(statement);
302
303 Class<?> testClass = description.getTestClass();
304
305 _afterMethodKeys = getMethodKeys(testClass, After.class);
306 _beforeMethodKeys = getMethodKeys(testClass, Before.class);
307 _newClassLoader = createClassLoader(description);
308 _testClassName = testClass.getName();
309 _testMethodKey = new MethodKey(
310 testClass, description.getMethodName());
311 }
312
313 @Override
314 public void evaluate() throws Throwable {
315 MethodCache.reset();
316
317 Thread currentThread = Thread.currentThread();
318
319 ClassLoader contextClassLoader =
320 currentThread.getContextClassLoader();
321
322 currentThread.setContextClassLoader(_newClassLoader);
323
324 String quiet = System.getProperty(
325 SystemProperties.SYSTEM_PROPERTIES_QUIET);
326
327 System.setProperty(
328 SystemProperties.SYSTEM_PROPERTIES_QUIET, StringPool.TRUE);
329
330 try {
331 Class<?> clazz = _newClassLoader.loadClass(_testClassName);
332
333 Object object = clazz.newInstance();
334
335 for (MethodKey beforeMethodKey : _beforeMethodKeys) {
336 invoke(_newClassLoader, beforeMethodKey, object);
337 }
338
339 invoke(_newClassLoader, _testMethodKey, object);
340
341 for (MethodKey afterMethodKey : _afterMethodKeys) {
342 invoke(_newClassLoader, afterMethodKey, object);
343 }
344 }
345 catch (InvocationTargetException ite) {
346 throw ite.getTargetException();
347 }
348 finally {
349 if (quiet == null) {
350 System.clearProperty(
351 SystemProperties.SYSTEM_PROPERTIES_QUIET);
352 }
353 else {
354 System.setProperty(
355 SystemProperties.SYSTEM_PROPERTIES_QUIET, quiet);
356 }
357
358 currentThread.setContextClassLoader(contextClassLoader);
359
360 MethodCache.reset();
361 }
362 }
363
364 private final List<MethodKey> _afterMethodKeys;
365 private final List<MethodKey> _beforeMethodKeys;
366 private final ClassLoader _newClassLoader;
367 private final String _testClassName;
368 private final MethodKey _testMethodKey;
369
370 }
371
372 private class RunInNewJVMStatment extends StatementWrapper {
373
374 public RunInNewJVMStatment(
375 ProcessConfig processConfig, Statement statement,
376 Description description) {
377
378 super(statement);
379
380 _processConfig = processConfig;
381
382 Class<?> testClass = description.getTestClass();
383
384 _afterMethodKeys = getMethodKeys(testClass, After.class);
385 _beforeMethodKeys = getMethodKeys(testClass, Before.class);
386 _testClassName = testClass.getName();
387 _testMethodKey = new MethodKey(
388 testClass, description.getMethodName());
389 }
390
391 @Override
392 public void evaluate() throws Throwable {
393 ProcessCallable<Serializable> processCallable =
394 new TestProcessCallable(
395 _testClassName, _beforeMethodKeys, _testMethodKey,
396 _afterMethodKeys);
397
398 processCallable = processProcessCallable(
399 processCallable, _testMethodKey);
400
401 ProcessChannel<Serializable> processChannel =
402 _processExecutor.execute(_processConfig, processCallable);
403
404 Future<Serializable> future =
405 processChannel.getProcessNoticeableFuture();
406
407 try {
408 future.get();
409 }
410 catch (ExecutionException ee) {
411 Throwable cause = ee.getCause();
412
413 while ((cause instanceof ProcessException) ||
414 (cause instanceof InvocationTargetException)) {
415
416 cause = cause.getCause();
417 }
418
419 throw cause;
420 }
421 }
422
423 private final List<MethodKey> _afterMethodKeys;
424 private final List<MethodKey> _beforeMethodKeys;
425 private final ProcessConfig _processConfig;
426 private final String _testClassName;
427 private final MethodKey _testMethodKey;
428
429 }
430
431 }