001
014
015 package com.liferay.portal.kernel.test.rule;
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.rule.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<>(frameworkMethods.size());
124
125 for (FrameworkMethod annotatedFrameworkMethod : frameworkMethods) {
126 methodKeys.add(new MethodKey(annotatedFrameworkMethod.getMethod()));
127 }
128
129 return methodKeys;
130 }
131
132 protected static void invoke(
133 ClassLoader classLoader, MethodKey methodKey, Object object)
134 throws Exception {
135
136 methodKey = methodKey.transform(classLoader);
137
138 Method method = methodKey.getMethod();
139
140 method.invoke(object);
141 }
142
143 protected NewEnvTestRule() {
144 }
145
146 protected List<String> createArguments(Description description) {
147 List<String> arguments = new ArrayList<>();
148
149 arguments.add("-Djava.net.preferIPv4Stack=true");
150
151 if (Boolean.getBoolean("junit.debug")) {
152 arguments.add(_JPDA_OPTIONS);
153 arguments.add("-Djunit.debug=true");
154 }
155
156 arguments.add("-Dliferay.mode=test");
157
158 String whipAgentLine = System.getProperty("whip.agent");
159
160 if (Validator.isNotNull(whipAgentLine)) {
161 arguments.add(whipAgentLine);
162 arguments.add("-Dwhip.agent=" + whipAgentLine);
163 }
164
165 String fileName = System.getProperty("whip.datafile");
166
167 if (fileName != null) {
168 arguments.add("-Dwhip.datafile=" + fileName);
169 }
170
171 if (Boolean.getBoolean("whip.instrument.dump")) {
172 arguments.add("-Dwhip.instrument.dump=true");
173 }
174
175 arguments.add("-Dwhip.static.instrument=true");
176
177 return arguments;
178 }
179
180 protected ClassLoader createClassLoader(Description description) {
181 try {
182 return new URLClassLoader(
183 ClassPathUtil.getClassPathURLs(CLASS_PATH), null);
184 }
185 catch (MalformedURLException murle) {
186 throw new RuntimeException(murle);
187 }
188 }
189
190 protected NewEnv findNewEnv(Description description) {
191 NewEnv newEnv = description.getAnnotation(NewEnv.class);
192
193 if (newEnv == null) {
194 Class<?> testClass = description.getTestClass();
195
196 newEnv = testClass.getAnnotation(NewEnv.class);
197 }
198
199 return newEnv;
200 }
201
202 protected ProcessCallable<Serializable> processProcessCallable(
203 ProcessCallable<Serializable> processCallable,
204 MethodKey testMethodKey) {
205
206 return processCallable;
207 }
208
209 protected static final String CLASS_PATH = ClassPathUtil.getJVMClassPath(
210 true);
211
212 private static final String _JPDA_OPTIONS =
213 "-agentlib:jdwp=transport=dt_socket,address=8001,server=y,suspend=y";
214
215 private static final ProcessExecutor _processExecutor =
216 new LocalProcessExecutor();
217
218 static {
219 Thread currentThread = Thread.currentThread();
220
221 ClassLoader contextClassLoader = currentThread.getContextClassLoader();
222
223 PortalClassLoaderUtil.setClassLoader(contextClassLoader);
224 }
225
226 private static class TestProcessCallable
227 implements ProcessCallable<Serializable> {
228
229 public TestProcessCallable(
230 String testClassName, List<MethodKey> beforeMethodKeys,
231 MethodKey testMethodKey, List<MethodKey> afterMethodKeys) {
232
233 _testClassName = testClassName;
234 _beforeMethodKeys = beforeMethodKeys;
235 _testMethodKey = testMethodKey;
236 _afterMethodKeys = afterMethodKeys;
237 }
238
239 @Override
240 public Serializable call() throws ProcessException {
241 attachProcess("Attached " + toString());
242
243 Thread currentThread = Thread.currentThread();
244
245 ClassLoader contextClassLoader =
246 currentThread.getContextClassLoader();
247
248 System.setProperty(
249 SystemProperties.SYSTEM_PROPERTIES_QUIET, StringPool.TRUE);
250
251 try {
252 Class<?> clazz = contextClassLoader.loadClass(_testClassName);
253
254 Object object = clazz.newInstance();
255
256 for (MethodKey beforeMethodKey : _beforeMethodKeys) {
257 invoke(contextClassLoader, beforeMethodKey, object);
258 }
259
260 invoke(contextClassLoader, _testMethodKey, object);
261
262 for (MethodKey afterMethodKey : _afterMethodKeys) {
263 invoke(contextClassLoader, afterMethodKey, object);
264 }
265 }
266 catch (Exception e) {
267 throw new ProcessException(e);
268 }
269
270 return StringPool.BLANK;
271 }
272
273 @Override
274 public String toString() {
275 StringBundler sb = new StringBundler(4);
276
277 sb.append(_testClassName);
278 sb.append(StringPool.PERIOD);
279 sb.append(_testMethodKey.getMethodName());
280 sb.append("()");
281
282 return sb.toString();
283 }
284
285 private static final long serialVersionUID = 1L;
286
287 private final List<MethodKey> _afterMethodKeys;
288 private final List<MethodKey> _beforeMethodKeys;
289 private final String _testClassName;
290 private final MethodKey _testMethodKey;
291
292 }
293
294 private class RunInNewClassLoaderStatement extends StatementWrapper {
295
296 public RunInNewClassLoaderStatement(
297 Statement statement, Description description) {
298
299 super(statement);
300
301 Class<?> testClass = description.getTestClass();
302
303 _afterMethodKeys = getMethodKeys(testClass, After.class);
304 _beforeMethodKeys = getMethodKeys(testClass, Before.class);
305 _newClassLoader = createClassLoader(description);
306 _testClassName = testClass.getName();
307 _testMethodKey = new MethodKey(
308 testClass, description.getMethodName());
309 }
310
311 @Override
312 public void evaluate() throws Throwable {
313 MethodCache.reset();
314
315 Thread currentThread = Thread.currentThread();
316
317 ClassLoader contextClassLoader =
318 currentThread.getContextClassLoader();
319
320 currentThread.setContextClassLoader(_newClassLoader);
321
322 String quiet = System.getProperty(
323 SystemProperties.SYSTEM_PROPERTIES_QUIET);
324
325 System.setProperty(
326 SystemProperties.SYSTEM_PROPERTIES_QUIET, StringPool.TRUE);
327
328 try {
329 Class<?> clazz = _newClassLoader.loadClass(_testClassName);
330
331 Object object = clazz.newInstance();
332
333 for (MethodKey beforeMethodKey : _beforeMethodKeys) {
334 invoke(_newClassLoader, beforeMethodKey, object);
335 }
336
337 invoke(_newClassLoader, _testMethodKey, object);
338
339 for (MethodKey afterMethodKey : _afterMethodKeys) {
340 invoke(_newClassLoader, afterMethodKey, object);
341 }
342 }
343 catch (InvocationTargetException ite) {
344 throw ite.getTargetException();
345 }
346 finally {
347 if (quiet == null) {
348 System.clearProperty(
349 SystemProperties.SYSTEM_PROPERTIES_QUIET);
350 }
351 else {
352 System.setProperty(
353 SystemProperties.SYSTEM_PROPERTIES_QUIET, quiet);
354 }
355
356 currentThread.setContextClassLoader(contextClassLoader);
357
358 MethodCache.reset();
359 }
360 }
361
362 private final List<MethodKey> _afterMethodKeys;
363 private final List<MethodKey> _beforeMethodKeys;
364 private final ClassLoader _newClassLoader;
365 private final String _testClassName;
366 private final MethodKey _testMethodKey;
367
368 }
369
370 private class RunInNewJVMStatment extends StatementWrapper {
371
372 public RunInNewJVMStatment(
373 ProcessConfig processConfig, Statement statement,
374 Description description) {
375
376 super(statement);
377
378 _processConfig = processConfig;
379
380 Class<?> testClass = description.getTestClass();
381
382 _afterMethodKeys = getMethodKeys(testClass, After.class);
383 _beforeMethodKeys = getMethodKeys(testClass, Before.class);
384 _testClassName = testClass.getName();
385 _testMethodKey = new MethodKey(
386 testClass, description.getMethodName());
387 }
388
389 @Override
390 public void evaluate() throws Throwable {
391 ProcessCallable<Serializable> processCallable =
392 new TestProcessCallable(
393 _testClassName, _beforeMethodKeys, _testMethodKey,
394 _afterMethodKeys);
395
396 processCallable = processProcessCallable(
397 processCallable, _testMethodKey);
398
399 ProcessChannel<Serializable> processChannel =
400 _processExecutor.execute(_processConfig, processCallable);
401
402 Future<Serializable> future =
403 processChannel.getProcessNoticeableFuture();
404
405 try {
406 future.get();
407 }
408 catch (ExecutionException ee) {
409 Throwable cause = ee.getCause();
410
411 while ((cause instanceof ProcessException) ||
412 (cause instanceof InvocationTargetException)) {
413
414 cause = cause.getCause();
415 }
416
417 throw cause;
418 }
419 }
420
421 private final List<MethodKey> _afterMethodKeys;
422 private final List<MethodKey> _beforeMethodKeys;
423 private final ProcessConfig _processConfig;
424 private final String _testClassName;
425 private final MethodKey _testMethodKey;
426
427 }
428
429 }