PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/core/petra/petra-process/src/test/java/com/liferay/petra/process/local/LocalProcessExecutorTest.java

http://github.com/liferay/liferay-portal
Java | 1882 lines | 1304 code | 524 blank | 54 comment | 49 complexity | 8d7581c1adff0a860085b7c01be66617 MD5 | raw file
Possible License(s): LGPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /**
  2. * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
  3. *
  4. * This library is free software; you can redistribute it and/or modify it under
  5. * the terms of the GNU Lesser General Public License as published by the Free
  6. * Software Foundation; either version 2.1 of the License, or (at your option)
  7. * any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  12. * details.
  13. */
  14. package com.liferay.petra.process.local;
  15. import com.liferay.petra.concurrent.NoticeableFuture;
  16. import com.liferay.petra.io.unsync.UnsyncByteArrayOutputStream;
  17. import com.liferay.petra.process.ProcessCallable;
  18. import com.liferay.petra.process.ProcessChannel;
  19. import com.liferay.petra.process.ProcessConfig;
  20. import com.liferay.petra.process.ProcessException;
  21. import com.liferay.petra.process.ProcessExecutor;
  22. import com.liferay.petra.process.ProcessLog;
  23. import com.liferay.petra.process.TerminationProcessException;
  24. import com.liferay.petra.reflect.ReflectionUtil;
  25. import com.liferay.petra.string.CharPool;
  26. import com.liferay.petra.string.StringBundler;
  27. import com.liferay.petra.string.StringPool;
  28. import com.liferay.petra.test.util.ThreadTestUtil;
  29. import com.liferay.portal.kernel.test.ReflectionTestUtil;
  30. import com.liferay.portal.kernel.test.rule.AggregateTestRule;
  31. import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor;
  32. import com.liferay.portal.test.rule.LiferayUnitTestRule;
  33. import java.io.EOFException;
  34. import java.io.File;
  35. import java.io.FileDescriptor;
  36. import java.io.FileOutputStream;
  37. import java.io.IOException;
  38. import java.io.NotSerializableException;
  39. import java.io.ObjectInputStream;
  40. import java.io.ObjectOutputStream;
  41. import java.io.OutputStream;
  42. import java.io.PrintStream;
  43. import java.io.Serializable;
  44. import java.io.StreamCorruptedException;
  45. import java.io.WriteAbortedException;
  46. import java.lang.reflect.Constructor;
  47. import java.net.InetAddress;
  48. import java.net.InetSocketAddress;
  49. import java.net.ServerSocket;
  50. import java.net.Socket;
  51. import java.net.URL;
  52. import java.net.URLClassLoader;
  53. import java.nio.channels.ClosedChannelException;
  54. import java.nio.channels.ServerSocketChannel;
  55. import java.util.ArrayList;
  56. import java.util.Arrays;
  57. import java.util.Collections;
  58. import java.util.HashMap;
  59. import java.util.List;
  60. import java.util.Map;
  61. import java.util.Objects;
  62. import java.util.concurrent.BlockingQueue;
  63. import java.util.concurrent.ExecutionException;
  64. import java.util.concurrent.Future;
  65. import java.util.concurrent.SynchronousQueue;
  66. import java.util.concurrent.TimeUnit;
  67. import java.util.concurrent.atomic.AtomicReference;
  68. import java.util.function.Consumer;
  69. import java.util.function.Supplier;
  70. import org.junit.Assert;
  71. import org.junit.ClassRule;
  72. import org.junit.Rule;
  73. import org.junit.Test;
  74. /**
  75. * @author Shuyang Zhou
  76. */
  77. public class LocalProcessExecutorTest {
  78. @ClassRule
  79. @Rule
  80. public static final AggregateTestRule aggregateTestRule =
  81. new AggregateTestRule(
  82. new CodeCoverageAssertor() {
  83. @Override
  84. public void appendAssertClasses(List<Class<?>> assertClasses) {
  85. assertClasses.add(ProcessConfig.class);
  86. Collections.addAll(
  87. assertClasses,
  88. ProcessConfig.class.getDeclaredClasses());
  89. assertClasses.add(LocalProcessLauncher.class);
  90. Collections.addAll(
  91. assertClasses,
  92. LocalProcessLauncher.class.getDeclaredClasses());
  93. }
  94. },
  95. LiferayUnitTestRule.INSTANCE);
  96. @Test
  97. public void testHeartBeatThreadDetachOnBrokenPipe() throws Exception {
  98. _testHearBeatThreadDetachByShutdownHook(
  99. Operations.SHUTDOWN_HOOK_TRIGGER_BROKEN_PIPE,
  100. ShutdownHooks.DETACH_ON_BROKEN_PIPE_SHUTDOWN_HOOK);
  101. }
  102. @Test
  103. public void testHeartBeatThreadDetachOnInterruption() throws Exception {
  104. _testHearBeatThreadDetachByShutdownHook(
  105. Operations.SHUTDOWN_HOOK_TRIGGER_INTERRUPTION,
  106. ShutdownHooks.DETACH_ON_INTERRUPTION_SHUTDOWN_HOOK);
  107. }
  108. @Test
  109. public void testHeartBeatThreadDetachOnUnknown() throws Exception {
  110. _testHearBeatThreadDetachByShutdownHook(
  111. Operations.SHUTDOWN_HOOK_TRIGGER_UNKNOWN,
  112. ShutdownHooks.DETACH_ON_UNKNOWN_SHUTDOWN_HOOK);
  113. }
  114. @Test
  115. public void testLocalProcessLauncherConstructor() {
  116. new LocalProcessLauncher();
  117. }
  118. @Test
  119. public void testProcessCallableWithException() throws Exception {
  120. // ProcessException
  121. ProcessChannel<Serializable> processChannel =
  122. _localProcessExecutor.execute(
  123. _createJPDAProcessConfig(_JPDA_OPTIONS1),
  124. () -> {
  125. throw new ProcessException("ROOT ProcessException");
  126. });
  127. NoticeableFuture<Serializable> noticeableFuture =
  128. processChannel.getProcessNoticeableFuture();
  129. try {
  130. noticeableFuture.get();
  131. Assert.fail();
  132. }
  133. catch (ExecutionException executionException) {
  134. Throwable throwable = executionException.getCause();
  135. Assert.assertSame(ProcessException.class, throwable.getClass());
  136. Assert.assertEquals(
  137. "ROOT ProcessException", throwable.getMessage());
  138. }
  139. // NullPointerException
  140. processChannel = _localProcessExecutor.execute(
  141. _createJPDAProcessConfig(_JPDA_OPTIONS1),
  142. () -> {
  143. throw new NullPointerException("ROOT NullPointerException");
  144. });
  145. noticeableFuture = processChannel.getProcessNoticeableFuture();
  146. try {
  147. noticeableFuture.get();
  148. Assert.fail();
  149. }
  150. catch (ExecutionException executionException) {
  151. Throwable throwable = executionException.getCause();
  152. Assert.assertSame(ProcessException.class, throwable.getClass());
  153. throwable = throwable.getCause();
  154. Assert.assertSame(NullPointerException.class, throwable.getClass());
  155. Assert.assertEquals(
  156. "ROOT NullPointerException", throwable.getMessage());
  157. }
  158. }
  159. @Test
  160. public void testProcessConfigBuilderEnvironment() throws Exception {
  161. // Default environment
  162. ProcessConfig.Builder builder = new ProcessConfig.Builder();
  163. builder.setArguments(_createArguments(_JPDA_OPTIONS1));
  164. builder.setBootstrapClassPath(System.getProperty("java.class.path"));
  165. builder.setReactClassLoader(
  166. LocalProcessExecutorTest.class.getClassLoader());
  167. ProcessChannel<HashMap<String, String>> processChannel =
  168. _localProcessExecutor.execute(
  169. builder.build(), Operations.GET_ENVIRONMENT);
  170. Future<HashMap<String, String>> future =
  171. processChannel.getProcessNoticeableFuture();
  172. Assert.assertEquals(System.getenv(), future.get());
  173. // New environment
  174. Map<String, String> environmentMap = new HashMap<>();
  175. environmentMap.put("key1", "value1");
  176. environmentMap.put("key2", "value2");
  177. builder.setEnvironment(environmentMap);
  178. processChannel = _localProcessExecutor.execute(
  179. builder.build(), Operations.GET_ENVIRONMENT);
  180. future = processChannel.getProcessNoticeableFuture();
  181. Map<String, String> actualEnvironmentMap = future.get();
  182. Assert.assertEquals("value1", actualEnvironmentMap.get("key1"));
  183. Assert.assertEquals("value2", actualEnvironmentMap.get("key2"));
  184. }
  185. @Test
  186. public void testProcessConfigBuilderJavaExecutable() throws Exception {
  187. try {
  188. ProcessConfig.Builder builder = new ProcessConfig.Builder();
  189. builder.setJavaExecutable("javax");
  190. _localProcessExecutor.execute(builder.build(), Operations.SLEEP);
  191. Assert.fail();
  192. }
  193. catch (ProcessException processException) {
  194. Throwable throwable = processException.getCause();
  195. Assert.assertTrue(throwable instanceof IOException);
  196. }
  197. }
  198. @Test
  199. public void testProcessConfigBuilderRuntimeClassPath() throws Exception {
  200. ProcessConfig.Builder builder = new ProcessConfig.Builder();
  201. builder.setArguments(_createArguments(_JPDA_OPTIONS1));
  202. char[] largeFileNameChars = new char[10 * 1024 * 1024];
  203. largeFileNameChars[0] = CharPool.SLASH;
  204. for (int i = 1; i < largeFileNameChars.length; i++) {
  205. largeFileNameChars[i] = (char)('a' + (i % 26));
  206. }
  207. String largeFileName = new String(largeFileNameChars);
  208. builder.setRuntimeClassPath(largeFileName);
  209. ProcessChannel<String> processChannel = _localProcessExecutor.execute(
  210. builder.build(), Operations.GET_RUNTIME_CLASS_PATH);
  211. Future<String> future = processChannel.getProcessNoticeableFuture();
  212. Assert.assertEquals(largeFileName, future.get());
  213. }
  214. @Test
  215. public void testProcessConfigCopyContructor() {
  216. String bootstrapClassPath = "bootstrapClassPath";
  217. String javaExecutable = "someJava";
  218. Consumer<ProcessLog> consumer = processLog -> {
  219. };
  220. ClassLoader reactClassLoader = new URLClassLoader(new URL[0]);
  221. String runtimeClassPath = "runtimeClassPath";
  222. ProcessConfig.Builder originalBuilder = new ProcessConfig.Builder();
  223. originalBuilder.setBootstrapClassPath(bootstrapClassPath);
  224. originalBuilder.setJavaExecutable(javaExecutable);
  225. originalBuilder.setProcessLogConsumer(consumer);
  226. originalBuilder.setReactClassLoader(reactClassLoader);
  227. originalBuilder.setRuntimeClassPath(runtimeClassPath);
  228. ProcessConfig originalProcessConfig = originalBuilder.build();
  229. // No arguments, no environment
  230. ProcessConfig.Builder copyBuilder1 = new ProcessConfig.Builder(
  231. originalProcessConfig);
  232. Assert.assertSame(Collections.emptyList(), copyBuilder1.getArguments());
  233. Assert.assertNull(copyBuilder1.getEnvironment());
  234. ProcessConfig copyProcessConfig1 = copyBuilder1.build();
  235. Assert.assertSame(
  236. Collections.emptyList(), copyProcessConfig1.getArguments());
  237. Assert.assertSame(
  238. bootstrapClassPath, copyProcessConfig1.getBootstrapClassPath());
  239. Assert.assertNull(copyProcessConfig1.getEnvironment());
  240. Assert.assertSame(
  241. javaExecutable, copyProcessConfig1.getJavaExecutable());
  242. Assert.assertSame(consumer, copyProcessConfig1.getProcessLogConsumer());
  243. Assert.assertSame(
  244. reactClassLoader, copyProcessConfig1.getReactClassLoader());
  245. Assert.assertSame(
  246. runtimeClassPath, copyProcessConfig1.getRuntimeClassPath());
  247. // With arguments and environment
  248. List<String> arguments = Arrays.asList("a", "b");
  249. Map<String, String> environment = new HashMap<>();
  250. environment.put("m", "n");
  251. environment.put("x", "y");
  252. originalBuilder.setArguments(arguments);
  253. originalBuilder.setEnvironment(environment);
  254. originalProcessConfig = originalBuilder.build();
  255. ProcessConfig.Builder copyBuilder2 = new ProcessConfig.Builder(
  256. originalProcessConfig);
  257. Assert.assertNotSame(arguments, copyBuilder2.getArguments());
  258. Assert.assertEquals(arguments, copyBuilder2.getArguments());
  259. Assert.assertNotSame(environment, copyBuilder2.getEnvironment());
  260. Assert.assertEquals(environment, copyBuilder2.getEnvironment());
  261. ProcessConfig copyProcessConfig2 = copyBuilder2.build();
  262. Assert.assertNotSame(arguments, copyProcessConfig2.getArguments());
  263. Assert.assertEquals(arguments, copyProcessConfig2.getArguments());
  264. Assert.assertSame(
  265. bootstrapClassPath, copyProcessConfig2.getBootstrapClassPath());
  266. Assert.assertNotSame(environment, copyProcessConfig2.getEnvironment());
  267. Assert.assertEquals(environment, copyProcessConfig2.getEnvironment());
  268. Assert.assertSame(
  269. javaExecutable, copyProcessConfig2.getJavaExecutable());
  270. Assert.assertSame(consumer, copyProcessConfig2.getProcessLogConsumer());
  271. Assert.assertSame(
  272. reactClassLoader, copyProcessConfig2.getReactClassLoader());
  273. Assert.assertSame(
  274. runtimeClassPath, copyProcessConfig2.getRuntimeClassPath());
  275. }
  276. @Test
  277. public void testProcessContextAttach() throws Exception {
  278. ProcessChannel<String> processChannel = _localProcessExecutor.execute(
  279. _createJPDAProcessConfig(_JPDA_OPTIONS1),
  280. Operations.asControllable(Operations.SLEEP));
  281. Future<Controller> future = processChannel.write(
  282. Operations.GET_CONTROLLER);
  283. Controller parentController = future.get();
  284. Assert.assertTrue(parentController.isAlive());
  285. Controller childController = parentController.invoke(
  286. Operations.asNewJVM(_JPDA_OPTIONS2, Operations.SLEEP));
  287. Assert.assertTrue(childController.isAlive());
  288. // Initially not attached
  289. Assert.assertFalse(childController.invoke(Operations.IS_ATTACHED));
  290. // Detach is not doing anything
  291. Assert.assertEquals("DONE", childController.invoke(Operations.DETACH));
  292. Assert.assertFalse(childController.invoke(Operations.IS_ATTACHED));
  293. // Attach child to parent
  294. Assert.assertTrue(
  295. childController.invoke(
  296. Operations.attach(ShutdownHooks.TERMINATE_SHUTDOWN_HOOK)));
  297. Assert.assertTrue(childController.invoke(Operations.IS_ATTACHED));
  298. // Double attach is rejected
  299. Assert.assertFalse(
  300. childController.invoke(
  301. Operations.attach(ShutdownHooks.TERMINATE_SHUTDOWN_HOOK)));
  302. // Detach
  303. Assert.assertEquals("DONE", childController.invoke(Operations.DETACH));
  304. Assert.assertFalse(childController.invoke(Operations.IS_ATTACHED));
  305. // Reattach
  306. Assert.assertTrue(
  307. childController.invoke(
  308. Operations.attach(ShutdownHooks.TERMINATE_SHUTDOWN_HOOK)));
  309. Assert.assertTrue(childController.invoke(Operations.IS_ATTACHED));
  310. // Kill parent
  311. parentController.invoke(Operations.TERMINATE);
  312. Assert.assertFalse(parentController.isAlive());
  313. NoticeableFuture<String> noticeableFuture =
  314. processChannel.getProcessNoticeableFuture();
  315. Assert.assertEquals("DONE", noticeableFuture.get());
  316. // Time wait 10 minutes to assert child is dead
  317. _timeWaitAssertFalse(
  318. "The child process is still alive", childController::isAlive, 10,
  319. TimeUnit.MINUTES);
  320. }
  321. @Test
  322. public void testProcessContextAttachWithNullShutdownHook()
  323. throws Exception {
  324. ProcessChannel<String> processChannel = _localProcessExecutor.execute(
  325. _createJPDAProcessConfig(_JPDA_OPTIONS1),
  326. Operations.asControllable(Operations.SLEEP));
  327. Future<Controller> future = processChannel.write(
  328. Operations.GET_CONTROLLER);
  329. Controller parentController = future.get();
  330. Assert.assertTrue(parentController.isAlive());
  331. Controller childController = parentController.invoke(
  332. Operations.asNewJVM(_JPDA_OPTIONS2, Operations.SLEEP));
  333. Assert.assertTrue(childController.isAlive());
  334. // Attach with null shutdown hook
  335. Assert.assertEquals(
  336. "DONE",
  337. childController.invoke(
  338. () -> {
  339. try {
  340. LocalProcessLauncher.ProcessContext.attach(
  341. "NullShutdownHook", 1, null);
  342. return "NULL_SHUTDOWN_HOOK_ACCEPTED";
  343. }
  344. catch (IllegalArgumentException illegalArgumentException) {
  345. if (!Objects.equals(
  346. illegalArgumentException.getMessage(),
  347. "Shutdown hook is null")) {
  348. return illegalArgumentException.getMessage();
  349. }
  350. }
  351. return "DONE";
  352. }));
  353. Assert.assertFalse(childController.invoke(Operations.IS_ATTACHED));
  354. // Kill parent
  355. parentController.invoke(Operations.TERMINATE);
  356. Assert.assertFalse(parentController.isAlive());
  357. // Kill child
  358. childController.invoke(Operations.TERMINATE);
  359. Assert.assertFalse(childController.isAlive());
  360. }
  361. @Test
  362. public void testProcessContextConstructor() throws Exception {
  363. Constructor<LocalProcessLauncher.ProcessContext> constructor =
  364. LocalProcessLauncher.ProcessContext.class.getDeclaredConstructor();
  365. constructor.setAccessible(true);
  366. constructor.newInstance();
  367. Assert.assertNotNull(
  368. LocalProcessLauncher.ProcessContext.getAttributes());
  369. }
  370. @Test
  371. public void testSpawnProcessWithoutAttach() throws Exception {
  372. ProcessChannel<String> processChannel = _localProcessExecutor.execute(
  373. _createJPDAProcessConfig(_JPDA_OPTIONS1),
  374. Operations.asControllable(Operations.SLEEP));
  375. Future<Controller> future = processChannel.write(
  376. Operations.GET_CONTROLLER);
  377. Controller parentController = future.get();
  378. Assert.assertTrue(parentController.isAlive());
  379. Controller childController = parentController.invoke(
  380. Operations.asNewJVM(_JPDA_OPTIONS2, Operations.SLEEP));
  381. Assert.assertTrue(childController.isAlive());
  382. // Kill parent
  383. parentController.invoke(Operations.TERMINATE);
  384. Assert.assertFalse(parentController.isAlive());
  385. NoticeableFuture<String> noticeableFuture =
  386. processChannel.getProcessNoticeableFuture();
  387. Assert.assertEquals("DONE", noticeableFuture.get());
  388. // Test alive 10 times for child process
  389. for (int i = 0; i < 10; i++) {
  390. Thread.sleep(100);
  391. Assert.assertTrue(childController.isAlive());
  392. }
  393. // Kill child
  394. childController.invoke(Operations.TERMINATE);
  395. Assert.assertFalse(childController.isAlive());
  396. }
  397. @Test
  398. public void testSubprocessReactorAbort() throws Exception {
  399. List<ProcessLog> processLogs = new ArrayList<>();
  400. ProcessConfig.Builder builder = new ProcessConfig.Builder();
  401. builder.setArguments(_createArguments(_JPDA_OPTIONS1));
  402. builder.setBootstrapClassPath(System.getProperty("java.class.path"));
  403. builder.setProcessLogConsumer(
  404. processLog -> {
  405. if (processLog.getLevel() == ProcessLog.Level.ERROR) {
  406. processLogs.add(processLog);
  407. }
  408. });
  409. builder.setReactClassLoader(new URLClassLoader(new URL[0], null));
  410. ProcessChannel<Boolean> processChannel = _localProcessExecutor.execute(
  411. builder.build(), Operations.IS_ATTACHED);
  412. Future<Boolean> future = processChannel.getProcessNoticeableFuture();
  413. try {
  414. future.get();
  415. Assert.fail();
  416. }
  417. catch (ExecutionException executionException) {
  418. Throwable throwable = executionException.getCause();
  419. Assert.assertSame(
  420. ClassNotFoundException.class, throwable.getClass());
  421. }
  422. Assert.assertEquals(processLogs.toString(), 1, processLogs.size());
  423. ProcessLog processLog = processLogs.get(0);
  424. Assert.assertEquals("Abort subprocess piping", processLog.getMessage());
  425. Throwable throwable = processLog.getThrowable();
  426. Assert.assertSame(ClassNotFoundException.class, throwable.getClass());
  427. }
  428. @Test
  429. public void testSubprocessReactorCorruptedStream() throws Exception {
  430. List<ProcessLog> processLogs = new ArrayList<>();
  431. ProcessChannel<Serializable> processChannel =
  432. _localProcessExecutor.execute(
  433. _createJPDAProcessConfig(
  434. _JPDA_OPTIONS1,
  435. processLog -> {
  436. if (processLog.getLevel() == ProcessLog.Level.ERROR) {
  437. processLogs.add(processLog);
  438. }
  439. }),
  440. Operations.CORRUPTED_STREAM);
  441. Future<Serializable> future =
  442. processChannel.getProcessNoticeableFuture();
  443. try {
  444. future.get();
  445. Assert.fail();
  446. }
  447. catch (ExecutionException executionException) {
  448. Throwable throwable = executionException.getCause();
  449. Assert.assertTrue(throwable instanceof ProcessException);
  450. Assert.assertEquals(
  451. "Corrupted object input stream", throwable.getMessage());
  452. throwable = throwable.getCause();
  453. Assert.assertSame(
  454. StreamCorruptedException.class, throwable.getClass());
  455. }
  456. Assert.assertFalse(future.isCancelled());
  457. Assert.assertTrue(future.isDone());
  458. Assert.assertEquals(processLogs.toString(), 1, processLogs.size());
  459. ProcessLog processLog = processLogs.get(0);
  460. String message = processLog.getMessage();
  461. int index = message.lastIndexOf(' ');
  462. Assert.assertTrue(index != -1);
  463. Assert.assertEquals(
  464. "Dumping content of corrupted object input stream to",
  465. message.substring(0, index));
  466. File file = new File(message.substring(index + 1));
  467. Assert.assertTrue(file.exists());
  468. file.delete();
  469. Throwable throwable = processLog.getThrowable();
  470. Assert.assertSame(StreamCorruptedException.class, throwable.getClass());
  471. }
  472. @Test
  473. public void testSubprocessReactorCrash() throws Exception {
  474. // One crash
  475. ProcessChannel<Serializable> processChannel =
  476. _localProcessExecutor.execute(
  477. _createJPDAProcessConfig(_JPDA_OPTIONS1),
  478. Operations.crashJVM(1));
  479. Future<Serializable> future =
  480. processChannel.getProcessNoticeableFuture();
  481. try {
  482. future.get();
  483. Assert.fail();
  484. }
  485. catch (ExecutionException executionException) {
  486. Throwable throwable = executionException.getCause();
  487. Assert.assertSame(
  488. TerminationProcessException.class, throwable.getClass());
  489. Assert.assertEquals(
  490. "Subprocess terminated with exit code 1",
  491. throwable.getMessage());
  492. TerminationProcessException terminationProcessException =
  493. (TerminationProcessException)throwable;
  494. Assert.assertEquals(1, terminationProcessException.getExitCode());
  495. }
  496. // Zero crash
  497. processChannel = _localProcessExecutor.execute(
  498. _createJPDAProcessConfig(_JPDA_OPTIONS1), Operations.crashJVM(0));
  499. future = processChannel.getProcessNoticeableFuture();
  500. try {
  501. future.get();
  502. Assert.fail();
  503. }
  504. catch (ExecutionException executionException) {
  505. Throwable throwable = executionException.getCause();
  506. Assert.assertSame(ProcessException.class, throwable.getClass());
  507. Assert.assertEquals(
  508. "Subprocess piping back ended prematurely",
  509. throwable.getMessage());
  510. throwable = throwable.getCause();
  511. Assert.assertSame(EOFException.class, throwable.getClass());
  512. }
  513. }
  514. @Test
  515. public void testSubprocessReactorKillByCancel() throws Exception {
  516. List<ProcessLog> processLogs = new ArrayList<>();
  517. ProcessChannel<String> processChannel = _localProcessExecutor.execute(
  518. _createJPDAProcessConfig(
  519. _JPDA_OPTIONS1,
  520. processLog -> {
  521. if (processLog.getLevel() == ProcessLog.Level.ERROR) {
  522. processLogs.add(processLog);
  523. }
  524. }),
  525. Operations.asControllable(Operations.SLEEP));
  526. Future<Controller> future = processChannel.write(
  527. Operations.GET_CONTROLLER);
  528. Controller controller = future.get();
  529. Assert.assertTrue(controller.isAlive());
  530. Map<String, Object> attributes =
  531. LocalProcessLauncher.ProcessContext.getAttributes();
  532. BlockingQueue<Thread> reactorThreadBlockingQueue =
  533. new SynchronousQueue<>();
  534. attributes.put(
  535. "reactorThreadBlockingQueue", reactorThreadBlockingQueue);
  536. controller.invoke(
  537. () -> {
  538. try {
  539. LocalProcessLauncher.ProcessContext.writeProcessCallable(
  540. () -> {
  541. Map<String, Object> localAttributes =
  542. LocalProcessLauncher.ProcessContext.
  543. getAttributes();
  544. BlockingQueue<Thread>
  545. localReactorThreadBlockingQueue =
  546. (BlockingQueue<Thread>)
  547. localAttributes.remove(
  548. "reactorThreadBlockingQueue");
  549. try {
  550. localReactorThreadBlockingQueue.put(
  551. Thread.currentThread());
  552. }
  553. catch (InterruptedException interruptedException) {
  554. throw new ProcessException(
  555. interruptedException);
  556. }
  557. return null;
  558. });
  559. }
  560. catch (IOException ioException) {
  561. throw new ProcessException(ioException);
  562. }
  563. return null;
  564. });
  565. Thread reactorThread = reactorThreadBlockingQueue.take();
  566. NoticeableFuture<String> noticeableFuture =
  567. processChannel.getProcessNoticeableFuture();
  568. noticeableFuture.cancel(false);
  569. reactorThread.join();
  570. Assert.assertEquals(processLogs.toString(), 1, processLogs.size());
  571. ProcessLog processLog = processLogs.get(0);
  572. Assert.assertEquals("Abort subprocess piping", processLog.getMessage());
  573. Throwable throwable = processLog.getThrowable();
  574. Assert.assertSame(IOException.class, throwable.getClass());
  575. }
  576. @Test
  577. public void testSubprocessReactorKillByInterruption() throws Exception {
  578. ProcessChannel<String> processChannel = _localProcessExecutor.execute(
  579. _createJPDAProcessConfig(_JPDA_OPTIONS1),
  580. Operations.asControllable(Operations.SLEEP));
  581. Future<Controller> future = processChannel.write(
  582. Operations.GET_CONTROLLER);
  583. Controller controller = future.get();
  584. Assert.assertTrue(controller.isAlive());
  585. Map<String, Object> attributes =
  586. LocalProcessLauncher.ProcessContext.getAttributes();
  587. BlockingQueue<Thread> reactorThreadBlockingQueue =
  588. new SynchronousQueue<>();
  589. attributes.put(
  590. "reactorThreadBlockingQueue", reactorThreadBlockingQueue);
  591. controller.invoke(
  592. () -> {
  593. try {
  594. LocalProcessLauncher.ProcessContext.writeProcessCallable(
  595. () -> {
  596. Map<String, Object> localAttributes =
  597. LocalProcessLauncher.ProcessContext.
  598. getAttributes();
  599. BlockingQueue<Thread>
  600. localReactorThreadBlockingQueue =
  601. (BlockingQueue<Thread>)
  602. localAttributes.remove(
  603. "reactorThreadBlockingQueue");
  604. try {
  605. localReactorThreadBlockingQueue.put(
  606. Thread.currentThread());
  607. }
  608. catch (InterruptedException interruptedException) {
  609. throw new ProcessException(
  610. interruptedException);
  611. }
  612. return null;
  613. });
  614. Object processOutputStream =
  615. ReflectionTestUtil.getFieldValue(
  616. LocalProcessLauncher.ProcessContext.class,
  617. "_processOutputStream");
  618. ReflectionTestUtil.invoke(
  619. processOutputStream, "close", new Class<?>[0]);
  620. }
  621. catch (IOException ioException) {
  622. throw new ProcessException(ioException);
  623. }
  624. return null;
  625. });
  626. Thread reactorThread = reactorThreadBlockingQueue.take();
  627. reactorThread.interrupt();
  628. NoticeableFuture<String> noticeableFuture =
  629. processChannel.getProcessNoticeableFuture();
  630. try {
  631. noticeableFuture.get();
  632. Assert.fail();
  633. }
  634. catch (ExecutionException executionException) {
  635. Throwable throwable = executionException.getCause();
  636. Assert.assertSame(ProcessException.class, throwable.getClass());
  637. Assert.assertEquals(
  638. "Forcibly killed subprocess on interruption",
  639. throwable.getMessage());
  640. throwable = throwable.getCause();
  641. Assert.assertSame(InterruptedException.class, throwable.getClass());
  642. }
  643. }
  644. @Test
  645. public void testSubprocessReactorLeadingLog() throws Exception {
  646. List<ProcessLog> processLogs = new ArrayList<>();
  647. AtomicReference<ProcessLog.Level> levelReference =
  648. new AtomicReference<>(ProcessLog.Level.WARN);
  649. Consumer<ProcessLog> processLogConsumer = processLog -> {
  650. ProcessLog.Level level = processLog.getLevel();
  651. if (level.compareTo(levelReference.get()) >= 0) {
  652. processLogs.add(processLog);
  653. }
  654. };
  655. // Warn level
  656. ProcessChannel<String> processChannel = _localProcessExecutor.execute(
  657. _createJPDAProcessConfig(_JPDA_OPTIONS1, processLogConsumer),
  658. Operations.LEADING_LOG);
  659. Future<String> future = processChannel.getProcessNoticeableFuture();
  660. Assert.assertEquals("DONE", future.get());
  661. Assert.assertEquals(processLogs.toString(), 1, processLogs.size());
  662. ProcessLog processLog = processLogs.remove(0);
  663. Assert.assertEquals(
  664. "Found corrupt leading log Leading log", processLog.getMessage());
  665. // Fine level
  666. levelReference.set(ProcessLog.Level.DEBUG);
  667. processChannel = _localProcessExecutor.execute(
  668. _createJPDAProcessConfig(_JPDA_OPTIONS1, processLogConsumer),
  669. Operations.LEADING_LOG);
  670. future = processChannel.getProcessNoticeableFuture();
  671. Assert.assertEquals("DONE", future.get());
  672. Assert.assertEquals(processLogs.toString(), 3, processLogs.size());
  673. processLog = processLogs.remove(0);
  674. Assert.assertEquals(
  675. "Found corrupt leading log Leading log", processLog.getMessage());
  676. processLog = processLogs.remove(0);
  677. String message = processLog.getMessage();
  678. Assert.assertTrue(
  679. message, message.contains("Invoked generic process callable"));
  680. processLog = processLogs.remove(0);
  681. message = processLog.getMessage();
  682. Assert.assertTrue(
  683. message, message.contains("Invoked generic process callable"));
  684. // Severe level
  685. levelReference.set(ProcessLog.Level.ERROR);
  686. processChannel = _localProcessExecutor.execute(
  687. _createJPDAProcessConfig(_JPDA_OPTIONS1, processLogConsumer),
  688. Operations.LEADING_LOG);
  689. future = processChannel.getProcessNoticeableFuture();
  690. Assert.assertEquals("DONE", future.get());
  691. Assert.assertTrue(processLogs.toString(), processLogs.isEmpty());
  692. }
  693. @Test
  694. public void testSubprocessReactorPipingBackExceptionProcessCallable()
  695. throws Exception {
  696. List<ProcessLog> processLogs = new ArrayList<>();
  697. ProcessChannel<Serializable> processChannel =
  698. _localProcessExecutor.execute(
  699. _createJPDAProcessConfig(
  700. _JPDA_OPTIONS1,
  701. processLog -> {
  702. if (processLog.getLevel() == ProcessLog.Level.ERROR) {
  703. processLogs.add(processLog);
  704. }
  705. }),
  706. Operations.PIPING_BACK_EXCEPTION_PROCESS_CALLABLE);
  707. NoticeableFuture<Serializable> noticeableFuture =
  708. processChannel.getProcessNoticeableFuture();
  709. Assert.assertNull(noticeableFuture.get());
  710. Assert.assertEquals(processLogs.toString(), 1, processLogs.size());
  711. ProcessLog processLog = processLogs.get(0);
  712. Assert.assertEquals(
  713. "Unable to invoke generic process callable",
  714. processLog.getMessage());
  715. Throwable throwable = processLog.getThrowable();
  716. Assert.assertSame(ProcessException.class, throwable.getClass());
  717. Assert.assertEquals(
  718. "Exception ProcessCallable", throwable.getMessage());
  719. }
  720. @Test
  721. public void testSubprocessReactorPipingBackNonprocessCallable()
  722. throws Exception {
  723. List<ProcessLog> processLogs = new ArrayList<>();
  724. ProcessChannel<Serializable> processChannel =
  725. _localProcessExecutor.execute(
  726. _createJPDAProcessConfig(
  727. _JPDA_OPTIONS1,
  728. processLog -> {
  729. if (processLog.getLevel() == ProcessLog.Level.INFO) {
  730. processLogs.add(processLog);
  731. }
  732. }),
  733. Operations.PIPING_BACK_NONPROCESS_CALLABLE);
  734. NoticeableFuture<Serializable> noticeableFuture =
  735. processChannel.getProcessNoticeableFuture();
  736. noticeableFuture.get();
  737. Assert.assertEquals(processLogs.toString(), 1, processLogs.size());
  738. ProcessLog processLog = processLogs.get(0);
  739. Assert.assertEquals(
  740. "Received a nonprocess callable piping back string piping back " +
  741. "object",
  742. processLog.getMessage());
  743. }
  744. @Test
  745. public void testSubprocessReactorPipingBackWriteAborted() throws Exception {
  746. List<ProcessLog> processLogs = new ArrayList<>();
  747. ProcessChannel<Serializable> processChannel =
  748. _localProcessExecutor.execute(
  749. _createJPDAProcessConfig(
  750. _JPDA_OPTIONS1,
  751. processLog -> {
  752. if (processLog.getLevel() == ProcessLog.Level.WARN) {
  753. processLogs.add(processLog);
  754. }
  755. }),
  756. Operations.PIPING_BACK_WRITE_ABORTED);
  757. NoticeableFuture<Serializable> noticeableFuture =
  758. processChannel.getProcessNoticeableFuture();
  759. try {
  760. noticeableFuture.get();
  761. Assert.fail();
  762. }
  763. catch (ExecutionException executionException) {
  764. Throwable throwable = executionException.getCause();
  765. Assert.assertSame(ProcessException.class, throwable.getClass());
  766. throwable = throwable.getCause();
  767. Assert.assertSame(
  768. NotSerializableException.class, throwable.getClass());
  769. Assert.assertEquals(processLogs.toString(), 1, processLogs.size());
  770. ProcessLog processLog = processLogs.get(0);
  771. Assert.assertEquals(
  772. "Caught a write aborted exception", processLog.getMessage());
  773. throwable = processLog.getThrowable();
  774. Assert.assertSame(
  775. WriteAbortedException.class, throwable.getClass());
  776. throwable = throwable.getCause();
  777. Assert.assertSame(
  778. NotSerializableException.class, throwable.getClass());
  779. }
  780. }
  781. private static List<String> _createArguments(String jpdaOptions) {
  782. List<String> arguments = new ArrayList<>();
  783. arguments.add("-D" + _SYSTEM_PROPERTIES_QUIET + "=true");
  784. if (Boolean.getBoolean("jvm.debug")) {
  785. arguments.add(jpdaOptions);
  786. arguments.add("-Djvm.debug=true");
  787. }
  788. arguments.add("-Dsun.zip.disableMemoryMapping=true");
  789. String whipAgentLine = System.getProperty("whip.agent");
  790. if ((whipAgentLine != null) && !whipAgentLine.isEmpty()) {
  791. arguments.add(whipAgentLine);
  792. arguments.add("-Dwhip.agent=" + whipAgentLine);
  793. }
  794. String fileName = System.getProperty("whip.datafile");
  795. if (fileName != null) {
  796. arguments.add("-Dwhip.datafile=" + fileName);
  797. }
  798. if (Boolean.getBoolean("whip.instrument.dump")) {
  799. arguments.add("-Dwhip.instrument.dump=true");
  800. }
  801. arguments.add("-Dwhip.static.instrument=true");
  802. arguments.add("-Dwhip.static.instrument.use.data.file=true");
  803. return arguments;
  804. }
  805. private static ProcessConfig _createJPDAProcessConfig(String jpdaOption) {
  806. return _createJPDAProcessConfig(jpdaOption, null);
  807. }
  808. private static ProcessConfig _createJPDAProcessConfig(
  809. String jpdaOption, Consumer<ProcessLog> processLogConsumer) {
  810. ProcessConfig.Builder builder = new ProcessConfig.Builder();
  811. builder.setArguments(_createArguments(jpdaOption));
  812. builder.setBootstrapClassPath(System.getProperty("java.class.path"));
  813. if (processLogConsumer != null) {
  814. builder.setProcessLogConsumer(processLogConsumer);
  815. }
  816. builder.setReactClassLoader(
  817. LocalProcessExecutorTest.class.getClassLoader());
  818. return builder.build();
  819. }
  820. private static ServerSocketChannel _createServerSocketChannel()
  821. throws IOException {
  822. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  823. int port = 12342;
  824. while (true) {
  825. try {
  826. ServerSocket serverSocket = serverSocketChannel.socket();
  827. serverSocket.setReuseAddress(true);
  828. serverSocket.bind(
  829. new InetSocketAddress(
  830. InetAddress.getByName("127.0.0.1"), port));
  831. return serverSocketChannel;
  832. }
  833. catch (IOException ioException) {
  834. port++;
  835. }
  836. }
  837. }
  838. private static Serializable _shutdown() {
  839. for (Thread thread : ThreadTestUtil.getThreads()) {
  840. if ((thread != null) && Objects.equals(thread.getName(), "main")) {
  841. thread.interrupt();
  842. try {
  843. thread.join();
  844. }
  845. catch (InterruptedException interruptedException) {
  846. ReflectionUtil.throwException(interruptedException);
  847. }
  848. break;
  849. }
  850. }
  851. // Force run shutdown hook to flush out code coverage data before
  852. // closing ServerSocket to prevent racing condition.
  853. try {
  854. ReflectionTestUtil.invoke(
  855. Class.forName("java.lang.ApplicationShutdownHooks"), "runHooks",
  856. new Class<?>[0]);
  857. }
  858. catch (ClassNotFoundException classNotFoundException) {
  859. ReflectionUtil.throwException(classNotFoundException);
  860. }
  861. Map<String, Object> attributes =
  862. LocalProcessLauncher.ProcessContext.getAttributes();
  863. while (true) {
  864. ServerSocket serverSocket = (ServerSocket)attributes.get(
  865. "SERVER_SOCKET");
  866. if (serverSocket == null) {
  867. continue;
  868. }
  869. try {
  870. serverSocket.close();
  871. }
  872. catch (IOException ioException) {
  873. ReflectionUtil.throwException(ioException);
  874. }
  875. break;
  876. }
  877. return null;
  878. }
  879. private void _testHearBeatThreadDetachByShutdownHook(
  880. ProcessCallable<? extends Serializable>
  881. shutdownHookTriggerProcessCallable,
  882. ShutdownHooks.SerializableShutdownHook serializableShutdownHook)
  883. throws Exception {
  884. ProcessChannel<String> processChannel = _localProcessExecutor.execute(
  885. _createJPDAProcessConfig(_JPDA_OPTIONS1),
  886. Operations.asControllable(Operations.SLEEP));
  887. Future<Controller> future = processChannel.write(
  888. Operations.GET_CONTROLLER);
  889. Controller parentController = future.get();
  890. Assert.assertTrue(parentController.isAlive());
  891. Controller childController = parentController.invoke(
  892. Operations.asNewJVM(_JPDA_OPTIONS2, Operations.SLEEP));
  893. Assert.assertTrue(childController.isAlive());
  894. // Attach child to parent
  895. Assert.assertTrue(
  896. childController.invoke(
  897. Operations.attach(serializableShutdownHook)));
  898. Assert.assertTrue(childController.invoke(Operations.IS_ATTACHED));
  899. // Trigger shutdown hook
  900. childController.invoke(shutdownHookTriggerProcessCallable);
  901. Assert.assertFalse(childController.invoke(Operations.IS_ATTACHED));
  902. // Make sure reattach is doable
  903. Assert.assertTrue(
  904. childController.invoke(
  905. Operations.attach(serializableShutdownHook)));
  906. Assert.assertTrue(childController.invoke(Operations.IS_ATTACHED));
  907. // Detach
  908. Assert.assertEquals("DONE", childController.invoke(Operations.DETACH));
  909. Assert.assertFalse(childController.invoke(Operations.IS_ATTACHED));
  910. // Kill parent
  911. parentController.invoke(Operations.TERMINATE);
  912. Assert.assertFalse(parentController.isAlive());
  913. // Kill child
  914. childController.invoke(Operations.TERMINATE);
  915. Assert.assertFalse(childController.isAlive());
  916. }
  917. private void _timeWaitAssertFalse(
  918. String message, Supplier<Boolean> supplier, long time,
  919. TimeUnit timeUnit) {
  920. long startTime = System.currentTimeMillis();
  921. while (true) {
  922. long value = timeUnit.convert(
  923. System.currentTimeMillis() - startTime, TimeUnit.MILLISECONDS);
  924. if (value >= time) {
  925. break;
  926. }
  927. if (!supplier.get()) {
  928. return;
  929. }
  930. }
  931. Assert.assertFalse(
  932. StringBundler.concat(
  933. "After waited ", time, " ", timeUnit, ". ", message),
  934. supplier.get());
  935. }
  936. private static final String _JPDA_OPTIONS1 =
  937. "-agentlib:jdwp=transport=dt_socket,address=8001,server=y,suspend=y";
  938. private static final String _JPDA_OPTIONS2 =
  939. "-agentlib:jdwp=transport=dt_socket,address=8002,server=y,suspend=y";
  940. private static final String _SYSTEM_PROPERTIES_QUIET =
  941. "system.properties.quiet";
  942. private final LocalProcessExecutor _localProcessExecutor =
  943. new LocalProcessExecutor();
  944. private static class Controller implements Serializable {
  945. public <T extends Serializable> T invoke(
  946. ProcessCallable<T> processCallable) {
  947. try (Socket socket = new Socket(
  948. InetAddress.getByName("127.0.0.1"), _serverPort)) {
  949. ObjectOutputStream objectOutputStream = new ObjectOutputStream(
  950. socket.getOutputStream());
  951. objectOutputStream.writeObject(processCallable);
  952. ObjectInputStream objectInputStream = new ObjectInputStream(
  953. socket.getInputStream());
  954. return (T)objectInputStream.readObject();
  955. }
  956. catch (Exception exception) {
  957. return ReflectionUtil.throwException(exception);
  958. }
  959. }
  960. public boolean isAlive() {
  961. try {
  962. return invoke(() -> true);
  963. }
  964. catch (Exception exception) {
  965. return false;
  966. }
  967. }
  968. private Controller(int serverPort) {
  969. _serverPort = serverPort;
  970. }
  971. private static final long serialVersionUID = 1L;
  972. private final int _serverPort;
  973. }
  974. private static class Operations {
  975. public static final ProcessCallable<Serializable> CORRUPTED_STREAM =
  976. () -> {
  977. UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
  978. new UnsyncByteArrayOutputStream();
  979. try (ObjectOutputStream objectOutputStream =
  980. new ObjectOutputStream(unsyncByteArrayOutputStream)) {
  981. objectOutputStream.writeObject(
  982. (ProcessCallable<String>)() -> "DONE");
  983. }
  984. catch (Exception exception) {
  985. throw new ProcessException(exception);
  986. }
  987. byte[] serializedData =
  988. unsyncByteArrayOutputStream.toByteArray();
  989. serializedData[5] = (byte)(serializedData[5] + 1);
  990. try {
  991. FileOutputStream fileOutputStream = new FileOutputStream(
  992. FileDescriptor.out);
  993. fileOutputStream.write(serializedData);
  994. fileOutputStream.flush();
  995. }
  996. catch (Exception exception) {
  997. throw new ProcessException(exception);
  998. }
  999. return null;
  1000. };
  1001. public static final ProcessCallable<String> DETACH = () -> {
  1002. try {
  1003. LocalProcessLauncher.ProcessContext.detach();
  1004. }
  1005. catch (InterruptedException interruptedException) {
  1006. throw new ProcessException(interruptedException);
  1007. }
  1008. return "DONE";
  1009. };
  1010. public static final ProcessCallable<Controller> GET_CONTROLLER = () -> {
  1011. Map<String, Object> attributes =
  1012. LocalProcessLauncher.ProcessContext.getAttributes();
  1013. while (true) {
  1014. ServerSocket serverSocket = (ServerSocket)attributes.get(
  1015. "SERVER_SOCKET");
  1016. if (serverSocket == null) {
  1017. continue;
  1018. }
  1019. return new Controller(serverSocket.getLocalPort());
  1020. }
  1021. };
  1022. public static final ProcessCallable<HashMap<String, String>>
  1023. GET_ENVIRONMENT = () -> new HashMap<>(System.getenv());
  1024. public static final ProcessCallable<String> GET_RUNTIME_CLASS_PATH =
  1025. () -> {
  1026. Thread currentThread = Thread.currentThread();
  1027. URLClassLoader urlClassLoader =
  1028. (URLClassLoader)currentThread.getContextClassLoader();
  1029. URL[] urls = urlClassLoader.getURLs();
  1030. StringBundler sb = new StringBundler(urls.length * 2);
  1031. for (URL url : urls) {
  1032. String path = url.getPath();
  1033. int index = path.indexOf(":/");
  1034. if (index != -1) {
  1035. path = path.substring(index + 1);
  1036. }
  1037. if (path.endsWith(StringPool.SLASH)) {
  1038. path = path.substring(0, path.length() - 1);
  1039. }
  1040. sb.append(path);
  1041. sb.append(File.pathSeparator);
  1042. }
  1043. if (sb.index() > 0) {
  1044. sb.setIndex(sb.index() - 1);
  1045. }
  1046. return sb.toString();
  1047. };
  1048. public static final ProcessCallable<Boolean> IS_ATTACHED =
  1049. () -> LocalProcessLauncher.ProcessContext.isAttached();
  1050. public static final ProcessCallable<String> LEADING_LOG = () -> {
  1051. try {
  1052. FileOutputStream fileOutputStream = new FileOutputStream(
  1053. FileDescriptor.out);
  1054. fileOutputStream.write("Leading log".getBytes(StringPool.UTF8));
  1055. fileOutputStream.flush();
  1056. System.out.print("Body STDOUT log");
  1057. System.out.flush();
  1058. System.err.print("Body STDERR log");
  1059. System.err.flush();
  1060. // Forcibly restore System.out. This is a necessary protection
  1061. // for code coverage. Cobertura's collector thread will output
  1062. // to System.out after the subprocess's main thread has exited.
  1063. // That information will be captured by the parent unit test
  1064. // process which will cause an assert Assert.failure.
  1065. System.setOut(new PrintStream(fileOutputStream));
  1066. }
  1067. catch (Exception exception) {
  1068. throw new ProcessException(exception);
  1069. }
  1070. return "DONE";
  1071. };
  1072. public static final ProcessCallable<Serializable>
  1073. PIPING_BACK_EXCEPTION_PROCESS_CALLABLE = () -> {
  1074. try {
  1075. LocalProcessLauncher.ProcessContext.writeProcessCallable(
  1076. () -> {
  1077. throw new ProcessException(
  1078. "Exception ProcessCallable");
  1079. });
  1080. }
  1081. catch (IOException ioException) {
  1082. throw new ProcessException(ioException);
  1083. }
  1084. return null;
  1085. };
  1086. public static final ProcessCallable<Serializable>
  1087. PIPING_BACK_NONPROCESS_CALLABLE = () -> {
  1088. try {
  1089. UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
  1090. new UnsyncByteArrayOutputStream();
  1091. try (ObjectOutputStream objectOutputStream =
  1092. new ObjectOutputStream(
  1093. unsyncByteArrayOutputStream) {
  1094. @Override
  1095. protected void writeStreamHeader() {
  1096. }
  1097. }) {
  1098. objectOutputStream.reset();
  1099. objectOutputStream.writeUnshared(
  1100. "string piping back object");
  1101. }
  1102. synchronized (System.out) {
  1103. System.out.flush();
  1104. OutputStream outputStream = new FileOutputStream(
  1105. FileDescriptor.out);
  1106. outputStream.write(
  1107. unsyncByteArrayOutputStream.toByteArray());
  1108. }
  1109. }
  1110. catch (IOException ioException) {
  1111. throw new ProcessException(ioException);
  1112. }
  1113. return null;
  1114. };
  1115. public static final ProcessCallable<Serializable>
  1116. PIPING_BACK_WRITE_ABORTED = () -> {
  1117. try {
  1118. Object object = new Object();
  1119. LocalProcessLauncher.ProcessContext.writeProcessCallable(
  1120. () -> (Serializable)object);
  1121. }
  1122. catch (IOException ioException) {
  1123. throw new ProcessException(ioException);
  1124. }
  1125. return null;
  1126. };
  1127. public static final ProcessCallable<Serializable>
  1128. SHUTDOWN_HOOK_TRIGGER_BROKEN_PIPE = () -> {
  1129. AtomicReference<? extends Thread> heartbeatThreadReference =
  1130. ReflectionTestUtil.getFieldValue(
  1131. LocalProcessLauncher.ProcessContext.class,
  1132. "_heartbeatThreadAtomicReference");
  1133. Thread heartBeatThread = heartbeatThreadReference.get();
  1134. Object processOutputStream = ReflectionTestUtil.getFieldValue(
  1135. LocalProcessLauncher.ProcessContext.class,
  1136. "_processOutputStream");
  1137. ObjectOutputStream objectOutputStream =
  1138. ReflectionTestUtil.getFieldValue(
  1139. processOutputStream, "_objectOutputStream");
  1140. try {
  1141. ReflectionTestUtil.setFieldValue(
  1142. processOutputStream, "_objectOutputStream",
  1143. new ObjectOutputStream(
  1144. new UnsyncByteArrayOutputStream()) {
  1145. @Override
  1146. public void flush() throws IOException {
  1147. ReflectionTestUtil.setFieldValue(
  1148. processOutputStream, "_objectOutputStream",
  1149. objectOutputStream);
  1150. throw new IOException();
  1151. }
  1152. });
  1153. }
  1154. catch (IOException ioException) {
  1155. throw new ProcessException(ioException);
  1156. }
  1157. try {
  1158. heartBeatThread.join();
  1159. }
  1160. catch (InterruptedException interruptedException) {
  1161. throw new ProcessException(interruptedException);
  1162. }
  1163. return null;
  1164. };
  1165. public static final ProcessCallable<Serializable>
  1166. SHUTDOWN_HOOK_TRIGGER_INTERRUPTION = () -> {
  1167. AtomicReference<? extends Thread> heartbeatThreadReference =
  1168. ReflectionTestUtil.getFieldValue(
  1169. LocalProcessLauncher.ProcessContext.class,
  1170. "_heartbeatThreadAtomicReference");
  1171. Thread heartBeatThread = heartbeatThreadReference.get();
  1172. heartBeatThread.interrupt();
  1173. try {
  1174. heartBeatThread.join();
  1175. }
  1176. catch (InterruptedException interruptedException) {
  1177. throw new ProcessException(interruptedException);
  1178. }
  1179. return null;
  1180. };
  1181. public static final ProcessCallable<Serializable>
  1182. SHUTDOWN_HOOK_TRIGGER_UNKNOWN = () -> {
  1183. AtomicReference<? extends Thread> heartbeatThreadReference =
  1184. ReflectionTestUtil.getFieldValue(
  1185. LocalProcessLauncher.ProcessContext.class,
  1186. "_heartbeatThreadAtomicReference");
  1187. Thread heartBeatThread = heartbeatThreadReference.get();
  1188. Object processOutputStream = ReflectionTestUtil.getFieldValue(
  1189. LocalProcessLauncher.ProcessContext.class,
  1190. "_processOutputStream");
  1191. ObjectOutputStream objectOutputStream =
  1192. ReflectionTestUtil.getFieldValue(
  1193. processOutputStream, "_objectOutputStream");
  1194. try {
  1195. ReflectionTestUtil.setFieldValue(
  1196. processOutputStream, "_objectOutputStream",
  1197. new ObjectOutputStream(
  1198. new UnsyncByteArrayOutputStream()) {
  1199. @Override
  1200. public void flush() {
  1201. ReflectionTestUtil.setFieldValue(
  1202. processOutputStream, "_objectOutputStream",
  1203. objectOutputStream);
  1204. throw new NullPointerException();
  1205. }
  1206. });
  1207. }
  1208. catch (IOException ioException) {
  1209. throw new ProcessException(ioException);
  1210. }
  1211. try {
  1212. heartBeatThread.join();
  1213. }
  1214. catch (InterruptedException interruptedException) {
  1215. throw new ProcessException(interruptedException);
  1216. }
  1217. return null;
  1218. };
  1219. public static final ProcessCallable<String> SLEEP = () -> {
  1220. try {
  1221. Thread.sleep(Long.MAX_VALUE);
  1222. }
  1223. catch (InterruptedException interruptedException) {
  1224. }
  1225. return "DONE";
  1226. };
  1227. public static final ProcessCallable<Serializable> TERMINATE =
  1228. LocalProcessExecutorTest::_shutdown;
  1229. public static <T extends Serializable> ProcessCallable<T>
  1230. asControllable(ProcessCallable<T> processCallable) {
  1231. return () -> {
  1232. Map<String, Object> attributes =
  1233. LocalProcessLauncher.ProcessContext.getAttributes();
  1234. try {
  1235. ServerSocketChannel serverSocketChannel =
  1236. _createServerSocketChannel();
  1237. ServerSocket serverSocket = serverSocketChannel.socket();
  1238. attributes.put("SERVER_SOCKET", serverSocket);
  1239. Thread serverThread = new Thread(
  1240. () -> {
  1241. while (true) {
  1242. try (Socket socket = serverSocket.accept()) {
  1243. ObjectInputStream objectInputStream =
  1244. new ObjectInputStream(
  1245. socket.getInputStream());
  1246. ProcessCallable<Serializable>
  1247. requestProcessCallable =
  1248. (ProcessCallable<Serializable>)
  1249. objectInputStream.readObject();
  1250. ObjectOutputStream objectOutputStream =
  1251. new ObjectOutputStream(
  1252. socket.getOutputStream());
  1253. objectOutputStream.writeObject(
  1254. requestProcessCallable.call());
  1255. }
  1256. catch (ClosedChannelException
  1257. closedChannelException) {
  1258. return;
  1259. }
  1260. catch (Exception exception) {
  1261. exception.printStackTrace();
  1262. System.exit(10);
  1263. }
  1264. }
  1265. },
  1266. processCallable.toString() + "-Controller-Server");
  1267. serverThread.start();
  1268. return processCallable.call();
  1269. }
  1270. catch (IOException ioException) {
  1271. throw new ProcessException(ioException);
  1272. }
  1273. };
  1274. }
  1275. public static ProcessCallable<Controller> asNewJVM(
  1276. String jpdaOption, ProcessCallable<?> processCallable) {
  1277. return () -> {
  1278. ProcessExecutor processExecutor = new LocalProcessExecutor();
  1279. try {
  1280. ProcessChannel<?> processChannel = processExecutor.execute(
  1281. _createJPDAProcessConfig(jpdaOption),
  1282. asControllable(processCallable));
  1283. Future<Controller> childControllerFuture =
  1284. processChannel.write(Operations.GET_CONTROLLER);
  1285. return childControllerFuture.get();
  1286. }
  1287. catch (Exception exception) {
  1288. throw new ProcessException(exception);
  1289. }
  1290. };
  1291. }
  1292. public static ProcessCallable<Boolean> attach(
  1293. LocalProcessLauncher.ShutdownHook shutdownHook) {
  1294. return () -> LocalProcessLauncher.ProcessContext.attach(
  1295. "Child Process", 1, shutdownHook);
  1296. }
  1297. public static ProcessCallable<Serializable> crashJVM(int exitCode) {
  1298. return () -> {
  1299. System.exit(exitCode);
  1300. return null;
  1301. };
  1302. }
  1303. }
  1304. private static class ShutdownHooks {
  1305. public static final SerializableShutdownHook
  1306. DETACH_ON_BROKEN_PIPE_SHUTDOWN_HOOK =
  1307. (shutdownCode, shutdownThrowable) -> {
  1308. if ((shutdownCode ==
  1309. LocalProcessLauncher.ShutdownHook.
  1310. BROKEN_PIPE_CODE) &&
  1311. (shutdownThrowable instanceof IOException)) {
  1312. _unregisterHeartBeatThread();
  1313. return true;
  1314. }
  1315. return false;
  1316. };
  1317. public static final SerializableShutdownHook
  1318. DETACH_ON_INTERRUPTION_SHUTDOWN_HOOK =
  1319. (shutdownCode, shutdownThrowable) -> {
  1320. if ((shutdownCode ==
  1321. LocalProcessLauncher.ShutdownHook.
  1322. INTERRUPTION_CODE) &&
  1323. (shutdownThrowable.getClass() ==
  1324. InterruptedException.class)) {
  1325. _unregisterHeartBeatThread();
  1326. return true;
  1327. }
  1328. return false;
  1329. };
  1330. public static final SerializableShutdownHook
  1331. DETACH_ON_UNKNOWN_SHUTDOWN_HOOK =
  1332. (shutdownCode, shutdownThrowable) -> {
  1333. if ((shutdownCode ==
  1334. LocalProcessLauncher.ShutdownHook.UNKNOWN_CODE) &&
  1335. !(shutdownThrowable instanceof InterruptedException) &&
  1336. !(shutdownThrowable instanceof IOException)) {
  1337. _unregisterHeartBeatThread();
  1338. return true;
  1339. }
  1340. return false;
  1341. };
  1342. public static final SerializableShutdownHook TERMINATE_SHUTDOWN_HOOK =
  1343. (shutdownCode, shutd

Large files files are truncated, but you can click here to view the full file