/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.gemfire; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.gemfire.fork.CqCacheServerProcess; import org.springframework.data.gemfire.fork.FunctionCacheServerProcess; import org.springframework.data.gemfire.test.support.IOUtils; import org.springframework.data.gemfire.test.support.ThreadUtils; import org.springframework.util.StringUtils; /** * Utility class used to fork Java, JVM processes. * * @author Costin Leau * @author John Blum * @deprecated ForkUtils has serious design flaws; please use ProcessExecutor instead */ @Deprecated public class ForkUtil { private static final long TIMEOUT = 30000L; @SuppressWarnings("all") private static final Logger logger = LoggerFactory.getLogger(ForkUtil.class); private static OutputStream processStandardInStream; private static String JAVA_CLASSPATH = System.getProperty("java.class.path"); private static String JAVA_HOME = System.getProperty("java.home"); private static String JAVA_EXE = JAVA_HOME.concat(File.separator).concat("bin").concat(File.separator).concat("java"); private static String TEMP_DIRECTORY = System.getProperty("java.io.tmpdir"); private static List<String> buildJavaCommand(String arguments) { return buildJavaCommand(arguments.split("\\s+")); } private static List<String> buildJavaCommand(String[] args) { List<String> command = new ArrayList<>(args.length + 5); command.add(JAVA_EXE); command.add("-server"); command.add("-ea"); command.add("-classpath"); command.add(JAVA_CLASSPATH); command.addAll(Arrays.asList(args)); return command; } private static OutputStream cloneJVM(String arguments) { AtomicBoolean runCondition = new AtomicBoolean(true); List<String> command = buildJavaCommand(arguments); Process javaProcess; try { javaProcess = Runtime.getRuntime().exec(command.toArray(new String[command.size()])); logger.debug("Started fork from command: {}", StringUtils.arrayToDelimitedString(command.toArray()," ")); captureProcessStreams(runCondition, javaProcess); registerShutdownHook(runCondition, javaProcess); processStandardInStream = javaProcess.getOutputStream(); return processStandardInStream; } catch (IOException e) { throw new RuntimeException(String.format("Failed to fork JVM process using command [%s]", StringUtils.arrayToDelimitedString(command.toArray(), " ")), e); } } private static void captureProcessStreams(AtomicBoolean runCondition, Process javaProcess) { startNewThread(newProcessStreamReader(runCondition, javaProcess.getErrorStream(), "[FORK-ERR]")); startNewThread(newProcessStreamReader(runCondition, javaProcess.getInputStream(), "[FORK-OUT]")); } private static Thread startNewThread(Runnable runnable) { Thread runnableThread = new Thread(runnable); runnableThread.setDaemon(true); runnableThread.setPriority(Thread.NORM_PRIORITY); runnableThread.start(); return runnableThread; } private static Runnable newProcessStreamReader(AtomicBoolean runCondition, InputStream processStream, String label) { return () -> { BufferedReader processStreamReader = null; try { processStreamReader = new BufferedReader(new InputStreamReader(processStream)); for (String line = "Reading..."; runCondition.get() && line != null; line = processStreamReader.readLine()) { logger.debug("{} {}", label, line); } } catch (Exception ignore) { } finally { IOUtils.close(processStreamReader); } }; } private static void registerShutdownHook(AtomicBoolean runCondition, Process javaProcess) { Runtime.getRuntime().addShutdownHook(new Thread(() -> { runCondition.set(false); processStandardInStream = null; try { javaProcess.destroyForcibly(); javaProcess.waitFor(); } catch (InterruptedException ignore) { Thread.currentThread().interrupt(); } })); } public static OutputStream cacheServer(Class<?> type) { return startCacheServer(type.getName()); } public static OutputStream cqCacheServer() { return cacheServer(CqCacheServerProcess.class); } public static OutputStream functionCacheServer() { return cacheServer(FunctionCacheServerProcess.class); } public static OutputStream startCacheServer(String args) { return startGemFireProcess(args, "cache server"); } protected static OutputStream startGemFireProcess(String args, String processName) { String className = args.split(" ")[0]; if (controlFileExists(className)) { deleteControlFile(className); } OutputStream outputStream = cloneJVM(args); long timeout = (System.currentTimeMillis() + TIMEOUT); while (!controlFileExists(className) && System.currentTimeMillis() < timeout) { ThreadUtils.sleep(500L); } if (!controlFileExists(className)) { throw new RuntimeException(String.format("Failed to fork %s", processName)); } return outputStream; } public static void sendSignal() { try { processStandardInStream.write("\n".getBytes()); processStandardInStream.flush(); } catch (IOException e) { logger.info("Cannot communicate with forked VM", e); } } public static boolean createControlFile( String name) throws IOException { return new File(TEMP_DIRECTORY + File.separator + name).createNewFile(); } public static boolean controlFileExists(String name) { return new File(TEMP_DIRECTORY + File.separator + name).isFile(); } public static boolean deleteControlFile(String name) { return new File(TEMP_DIRECTORY + File.separator + name).delete(); } }