You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by zh...@apache.org on 2017/08/11 17:26:50 UTC
[20/23] geode git commit: GEODE-3413: overhaul launcher and process
classes and tests
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/FileProcessController.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/FileProcessController.java b/geode-core/src/main/java/org/apache/geode/internal/process/FileProcessController.java
index c8ec49d..062cfb6 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/FileProcessController.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/FileProcessController.java
@@ -14,13 +14,10 @@
*/
package org.apache.geode.internal.process;
-import org.apache.commons.lang.StringUtils;
-import org.apache.geode.distributed.internal.DistributionConfig;
-import org.apache.geode.internal.i18n.LocalizedStrings;
-import org.apache.geode.internal.logging.LogService;
-import org.apache.geode.internal.process.ControlFileWatchdog.ControlRequestHandler;
-import org.apache.geode.lang.AttachAPINotFoundException;
-import org.apache.logging.log4j.Logger;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.apache.commons.lang.StringUtils.isBlank;
+import static org.apache.commons.lang.Validate.isTrue;
+import static org.apache.commons.lang.Validate.notNull;
import java.io.BufferedReader;
import java.io.File;
@@ -30,70 +27,72 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.process.ControlFileWatchdog.ControlRequestHandler;
+import org.apache.geode.lang.AttachAPINotFoundException;
+
/**
* Controls a {@link ControllableProcess} using files to communicate between processes.
*
* @since GemFire 8.0
*/
-public class FileProcessController implements ProcessController {
- private static final Logger logger = LogService.getLogger();
+class FileProcessController implements ProcessController {
- public static final String STATUS_TIMEOUT_PROPERTY =
- DistributionConfig.GEMFIRE_PREFIX + "FileProcessController.STATUS_TIMEOUT";
+ static final long DEFAULT_STATUS_TIMEOUT_MILLIS = 60 * 1000;
private final long statusTimeoutMillis;
- private final FileControllerParameters arguments;
+ private final FileControllerParameters parameters;
private final int pid;
/**
* Constructs an instance for controlling a local process.
*
- * @param arguments details about the controllable process
+ * @param parameters details about the controllable process
* @param pid process id identifying the process to control
*
* @throws IllegalArgumentException if pid is not a positive integer
*/
- public FileProcessController(final FileControllerParameters arguments, final int pid) {
- this(arguments, pid, Long.getLong(STATUS_TIMEOUT_PROPERTY, 60 * 1000), TimeUnit.MILLISECONDS);
+ FileProcessController(final FileControllerParameters parameters, final int pid) {
+ this(parameters, pid, DEFAULT_STATUS_TIMEOUT_MILLIS, MILLISECONDS);
}
/**
* Constructs an instance for controlling a local process.
*
- * @param arguments details about the controllable process
+ * @param parameters details about the controllable process
* @param pid process id identifying the process to control
* @param timeout the timeout that operations must complete within
* @param units the units of the timeout
*
* @throws IllegalArgumentException if pid is not a positive integer
*/
- public FileProcessController(final FileControllerParameters arguments, final int pid,
+ FileProcessController(final FileControllerParameters parameters, final int pid,
final long timeout, final TimeUnit units) {
- if (pid < 1) {
- throw new IllegalArgumentException("Invalid pid '" + pid + "' specified");
- }
+ notNull(parameters, "Invalid parameters '" + parameters + "' specified");
+ isTrue(pid > 0, "Invalid pid '" + pid + "' specified");
+ isTrue(timeout >= 0, "Invalid timeout '" + timeout + "' specified");
+ notNull(units, "Invalid units '" + units + "' specified");
+
this.pid = pid;
- this.arguments = arguments;
+ this.parameters = parameters;
this.statusTimeoutMillis = units.toMillis(timeout);
}
@Override
public int getProcessId() {
- return this.pid;
+ return pid;
}
@Override
public String status()
throws UnableToControlProcessException, IOException, InterruptedException, TimeoutException {
- return status(this.arguments.getWorkingDirectory(),
- this.arguments.getProcessType().getStatusRequestFileName(),
- this.arguments.getProcessType().getStatusFileName());
+ return status(parameters.getDirectory(), parameters.getProcessType().getStatusRequestFileName(),
+ parameters.getProcessType().getStatusFileName());
}
@Override
public void stop() throws UnableToControlProcessException, IOException {
- stop(this.arguments.getWorkingDirectory(),
- this.arguments.getProcessType().getStopRequestFileName());
+ stop(parameters.getDirectory(), parameters.getProcessType().getStopRequestFileName());
}
@Override
@@ -102,8 +101,12 @@ public class FileProcessController implements ProcessController {
LocalizedStrings.Launcher_ATTACH_API_NOT_FOUND_ERROR_MESSAGE.toLocalizedString());
}
+ long getStatusTimeoutMillis() {
+ return statusTimeoutMillis;
+ }
+
private void stop(final File workingDir, final String stopRequestFileName) throws IOException {
- final File stopRequestFile = new File(workingDir, stopRequestFileName);
+ File stopRequestFile = new File(workingDir, stopRequestFileName);
if (!stopRequestFile.exists()) {
stopRequestFile.createNewFile();
}
@@ -112,56 +115,40 @@ public class FileProcessController implements ProcessController {
private String status(final File workingDir, final String statusRequestFileName,
final String statusFileName) throws IOException, InterruptedException, TimeoutException {
// monitor for statusFile
- final File statusFile = new File(workingDir, statusFileName);
- final AtomicReference<String> statusRef = new AtomicReference<>();
-
- final ControlRequestHandler statusHandler = new ControlRequestHandler() {
- @Override
- public void handleRequest() throws IOException {
- // read the statusFile
- final BufferedReader reader = new BufferedReader(new FileReader(statusFile));
- final StringBuilder lines = new StringBuilder();
- try {
- String line = null;
- while ((line = reader.readLine()) != null) {
- lines.append(line);
- }
- } finally {
- statusRef.set(lines.toString());
- reader.close();
- }
+ File statusFile = new File(workingDir, statusFileName);
+ AtomicReference<String> statusRef = new AtomicReference<>();
+
+ ControlRequestHandler statusHandler = () -> {
+ // read the statusFile
+ StringBuilder lines = new StringBuilder();
+ try (BufferedReader reader = new BufferedReader(new FileReader(statusFile))) {
+ reader.lines().forEach(lines::append);
+ } finally {
+ statusRef.set(lines.toString());
}
};
- final ControlFileWatchdog statusFileWatchdog =
+ ControlFileWatchdog statusFileWatchdog =
new ControlFileWatchdog(workingDir, statusFileName, statusHandler, true);
statusFileWatchdog.start();
- final File statusRequestFile = new File(workingDir, statusRequestFileName);
+ File statusRequestFile = new File(workingDir, statusRequestFileName);
if (!statusRequestFile.exists()) {
statusRequestFile.createNewFile();
}
// if timeout invoke stop and then throw TimeoutException
- final long start = System.currentTimeMillis();
+ long start = System.currentTimeMillis();
while (statusFileWatchdog.isAlive()) {
Thread.sleep(10);
- if (System.currentTimeMillis() >= start + this.statusTimeoutMillis) {
- final TimeoutException te =
- new TimeoutException("Timed out waiting for process to create " + statusFile);
- try {
- statusFileWatchdog.stop();
- } catch (InterruptedException e) {
- logger.info("Interrupted while stopping status file watchdog.", e);
- } catch (RuntimeException e) {
- logger.info("Unexpected failure while stopping status file watchdog.", e);
- }
- throw te;
+ if (System.currentTimeMillis() >= start + statusTimeoutMillis) {
+ statusFileWatchdog.stop();
+ throw new TimeoutException("Timed out waiting for process to create " + statusFile);
}
}
- final String lines = statusRef.get();
- if (StringUtils.isBlank(lines)) {
+ String lines = statusRef.get();
+ if (isBlank(lines)) {
throw new IllegalStateException("Failed to read status file");
}
return lines;
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessController.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessController.java b/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessController.java
deleted file mode 100755
index fbea19e..0000000
--- a/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessController.java
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The ASF licenses this file to You 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.apache.geode.internal.process;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.lang.management.RuntimeMXBean;
-import java.util.Properties;
-import java.util.Set;
-import javax.management.InstanceNotFoundException;
-import javax.management.MBeanException;
-import javax.management.MBeanServerConnection;
-import javax.management.ObjectName;
-import javax.management.Query;
-import javax.management.QueryExp;
-import javax.management.ReflectionException;
-import javax.management.remote.JMXConnector;
-import javax.management.remote.JMXConnectorFactory;
-import javax.management.remote.JMXServiceURL;
-
-import org.apache.geode.internal.util.IOUtils;
-import com.sun.tools.attach.AgentInitializationException;
-import com.sun.tools.attach.AgentLoadException;
-import com.sun.tools.attach.AttachNotSupportedException;
-import com.sun.tools.attach.VirtualMachine;
-
-/**
- * Attaches to a local process to control it via JMX.
- *
- * @since GemFire 7.0
- * @deprecated as of 8.0 please use {@link ControllableProcess} instead
- */
-public class LocalProcessController {
-
- /** Property name for the JMX local connector address (from sun.management.Agent) */
- private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
- "com.sun.management.jmxremote.localConnectorAddress";
-
- private final int pid;
-
- protected JMXConnector jmxc;
- protected MBeanServerConnection server;
-
- /**
- * Constructs an instance for controlling a local process.
- *
- * @param pid process id identifying the process to attach to
- *
- * @throws IllegalArgumentException if pid is not a positive integer
- */
- public LocalProcessController(final int pid) {
- if (pid < 1) {
- throw new IllegalArgumentException("Invalid pid '" + pid + "' specified");
- }
- this.pid = pid;
- }
-
- /**
- * Constructs an instance for controlling a local process.
- *
- * @param pidFile file containing the pid of the process to attach to
- *
- * @throws FileNotFoundException if the specified file name is not found within the directory
- * @throws IOException if unable to read from the specified file
- * @throws IllegalArgumentException if the pid in the pidFile is not a positive integer
- * @throws NumberFormatException if the pid file does not contain a parsable integer
- */
- public LocalProcessController(final File pidFile) throws IOException {
- this(readPid(pidFile));
- }
-
- /**
- * Constructs an instance for controlling a local process.
- *
- * @param directory directory containing a file of name pidFileName
- * @param pidFilename name of the file containing the pid of the process to attach to
- *
- * @throws FileNotFoundException if the specified file name is not found within the directory
- * @throws IOException if an I/O error occurs
- * @throws IllegalArgumentException if the pid in the pidFile is not a positive integer
- * @throws IllegalStateException if dir is not an existing directory
- * @throws NumberFormatException if the pid file does not contain a parsable integer
- */
- public LocalProcessController(final File directory, final String pidFilename) throws IOException {
- this(readPid(directory, pidFilename));
- }
-
- /**
- * Connects to the process and tells it to shut down.
- *
- * @param namePattern the name pattern of the MBean to use for stopping
- * @param pidAttribute the name of the MBean attribute with the process id to compare against
- * @param stopMethod the name of the MBean operation to invoke
- * @param attributes the names of the MBean attributes to compare with expected values
- * @param values the expected values of the specified MBean attributes
- *
- * @throws ConnectionFailedException if there was a failure to connect to the local JMX connector
- * in the process
- * @throws IOException if a communication problem occurred when talking to the MBean server
- * @throws MBeanInvocationFailedException if failed to invoke stop on the MBean for any reason
- * @throws PidUnavailableException if parsing the pid from the RuntimeMXBean name fails
- */
- public void stop(final ObjectName namePattern, final String pidAttribute, final String stopMethod,
- final String[] attributes, final Object[] values) throws ConnectionFailedException,
- IOException, MBeanInvocationFailedException, PidUnavailableException {
- invokeOperationOnTargetMBean(namePattern, pidAttribute, stopMethod, attributes, values);
- }
-
- /**
- * Connects to the process and acquires its status.
- *
- * @param namePattern the name pattern of the MBean to use for stopping
- * @param pidAttribute the name of the MBean attribute with the process id to compare against
- * @param statusMethod the name of the MBean operation to invoke
- * @param attributes the names of the MBean attributes to compare with expected values
- * @param values the expected values of the specified MBean attributes
- *
- * @return string describing the status of the process
- *
- * @throws ConnectionFailedException if there was a failure to connect to the local JMX connector
- * in the process
- * @throws IOException if a communication problem occurred when talking to the MBean server
- * @throws MBeanInvocationFailedException if failed to invoke stop on the MBean for any reason
- * @throws PidUnavailableException if parsing the pid from the RuntimeMXBean name fails
- */
- public String status(final ObjectName namePattern, final String pidAttribute,
- final String statusMethod, final String[] attributes, final Object[] values)
- throws ConnectionFailedException, IOException, MBeanInvocationFailedException,
- PidUnavailableException {
- return invokeOperationOnTargetMBean(namePattern, pidAttribute, statusMethod, attributes, values)
- .toString();
- }
-
- /**
- * Connects to the process and use its MBean to stop it.
- *
- * @param namePattern the name pattern of the MBean to use for stopping
- * @param pidAttribute the name of the MBean attribute with the process id to compare against
- * @param methodName the name of the MBean operation to invoke
- * @param attributes the names of the MBean attributes to compare with expected values
- * @param values the expected values of the specified MBean attributes
- *
- * @throws ConnectionFailedException if there was a failure to connect to the local JMX connector
- * in the process
- * @throws IOException if a communication problem occurred when talking to the MBean server
- * @throws MBeanInvocationFailedException if failed to invoke stop on the MBean for any reason
- * @throws PidUnavailableException if parsing the pid from the RuntimeMXBean name fails
- */
- private Object invokeOperationOnTargetMBean(final ObjectName namePattern,
- final String pidAttribute, final String methodName, final String[] attributes,
- final Object[] values) throws ConnectionFailedException, IOException,
- MBeanInvocationFailedException, PidUnavailableException {
- ObjectName objectName = namePattern;
- connect();
- try {
- final QueryExp constraint = buildQueryExp(pidAttribute, attributes, values);
- final Set<ObjectName> mbeanNames = this.server.queryNames(namePattern, constraint);
-
- if (mbeanNames.isEmpty()) {
- throw new MBeanInvocationFailedException("Failed to find mbean matching '" + namePattern
- + "' with attribute '" + pidAttribute + "' of value '" + this.pid + "'");
- }
- if (mbeanNames.size() > 1) {
- throw new MBeanInvocationFailedException("Found more than one mbean matching '"
- + namePattern + "' with attribute '" + pidAttribute + "' of value '" + this.pid + "'");
- }
-
- objectName = mbeanNames.iterator().next();
- return invoke(objectName, methodName);
- } catch (InstanceNotFoundException e) {
- throw new MBeanInvocationFailedException(
- "Failed to invoke " + methodName + " on " + objectName, e);
- } catch (MBeanException e) {
- throw new MBeanInvocationFailedException(
- "Failed to invoke " + methodName + " on " + objectName, e);
- } catch (ReflectionException e) {
- throw new MBeanInvocationFailedException(
- "Failed to invoke " + methodName + " on " + objectName, e);
- } finally {
- disconnect();
- }
- }
-
- /**
- * Returns the process id (pid) of the process.
- *
- * @return the process id (pid) of the process
- */
- public int getProcessId() {
- return this.pid;
- }
-
- /**
- * Connects to the JMX agent in the local process.
- *
- * @throws ConnectionFailedException if there was a failure to connect to the local JMX connector
- * in the process
- * @throws IOException if the JDK management agent cannot be found and loaded
- */
- void connect() throws ConnectionFailedException, IOException {
- try {
- final JMXServiceURL jmxUrl = getJMXServiceURL();
- this.jmxc = JMXConnectorFactory.connect(jmxUrl);
- this.server = this.jmxc.getMBeanServerConnection();
- } catch (AttachNotSupportedException e) {
- throw new ConnectionFailedException("Failed to connect to process '" + this.pid + "'", e);
- }
- }
-
- /**
- * Disconnects from the JMX agent in the local process.
- */
- void disconnect() {
- this.server = null;
- if (this.jmxc != null) {
- try {
- this.jmxc.close();
- } catch (IOException e) {
- // ignore
- }
- }
- this.jmxc = null;
- }
-
- /**
- * Ensures that the other process identifies itself by the same pid used by this stopper to
- * connect to that process. NOT USED EXCEPT IN TEST.
- *
- * @return true if the pid matches
- *
- * @throws IllegalStateException if the other process identifies itself by a different pid
- * @throws IOException if a communication problem occurred when accessing the
- * MBeanServerConnection
- * @throws PidUnavailableException if parsing the pid from the RuntimeMXBean name fails
- */
- boolean checkPidMatches() throws IllegalStateException, IOException, PidUnavailableException {
- final RuntimeMXBean proxy = ManagementFactory.newPlatformMXBeanProxy(this.server,
- ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class);
- final int remotePid = ProcessUtils.identifyPid(proxy.getName());
- if (remotePid != this.pid) {
- throw new IllegalStateException(
- "Process has different pid '" + remotePid + "' than expected pid '" + this.pid + "'");
- } else {
- return true;
- }
- }
-
- /**
- * Uses the Attach API to connect to the local process and ensures that it has loaded the JMX
- * management agent. The JMXServiceURL identifying the local connector address for the JMX agent
- * in the process is returned.
- *
- * @return the address of the JMX API connector server for connecting to the local process
- *
- * @throws AttachNotSupportedException if unable to use the Attach API to connect to the process
- * @throws IOException if the JDK management agent cannot be found and loaded
- */
- private JMXServiceURL getJMXServiceURL() throws AttachNotSupportedException, IOException {
- String connectorAddress = null;
- final VirtualMachine vm = VirtualMachine.attach(String.valueOf(this.pid));
- try {
- Properties agentProps = vm.getAgentProperties();
- connectorAddress = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
-
- if (connectorAddress == null) {
- // need to load the management-agent and get the address
-
- final String javaHome = vm.getSystemProperties().getProperty("java.home");
-
- // assume java.home is JDK and look in JRE for agent
- String managementAgentPath = javaHome + File.separator + "jre" + File.separator + "lib"
- + File.separator + "management-agent.jar";
- File managementAgent = new File(managementAgentPath);
- if (!managementAgent.exists()) {
- // assume java.home is JRE and look in lib for agent
- managementAgentPath =
- javaHome + File.separator + "lib" + File.separator + "management-agent.jar";
- managementAgent = new File(managementAgentPath);
- if (!managementAgent.exists()) {
- throw new IOException("JDK management agent not found");
- }
- }
-
- // attempt to load the management agent
- managementAgentPath = managementAgent.getCanonicalPath();
- try {
- vm.loadAgent(managementAgentPath, "com.sun.management.jmxremote");
- } catch (AgentLoadException e) {
- IOException ioe = new IOException(e.getMessage());
- ioe.initCause(e);
- throw ioe;
- } catch (AgentInitializationException e) {
- IOException ioe = new IOException(e.getMessage());
- ioe.initCause(e);
- throw ioe;
- }
-
- // get the connector address
- agentProps = vm.getAgentProperties();
- connectorAddress = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
- }
- } finally {
- vm.detach();
- }
-
- if (connectorAddress == null) {
- // should never reach here
- throw new IOException("Failed to find address to attach to process");
- }
-
- return new JMXServiceURL(connectorAddress);
- }
-
- /**
- * Builds the QueryExp used to identify the target MBean.
- *
- * @param pidAttribute the name of the MBean attribute with the process id to compare against
- * @param attributes the names of additional MBean attributes to compare with expected values
- * @param values the expected values of the specified MBean attributes
- *
- * @return the main QueryExp for matching the target MBean
- */
- private QueryExp buildQueryExp(final String pidAttribute, final String[] attributes,
- final Object[] values) {
- final QueryExp optionalAttributes = buildOptionalQueryExp(attributes, values);
- QueryExp constraint;
- if (optionalAttributes != null) {
- constraint =
- Query.and(optionalAttributes, Query.eq(Query.attr(pidAttribute), Query.value(this.pid)));
- } else {
- constraint = Query.eq(Query.attr(pidAttribute), Query.value(this.pid));
- }
- return constraint;
- }
-
- /**
- * Builds an optional QueryExp to aid in matching the correct MBean using additional attributes
- * with the specified values. Returns null if no attributes and values were specified during
- * construction.
- *
- * @param attributes the names of additional MBean attributes to compare with expected values
- * @param values the expected values of the specified MBean attributes
- *
- * @return optional QueryExp to aid in matching the correct MBean
- */
- private QueryExp buildOptionalQueryExp(final String[] attributes, final Object[] values) {
- QueryExp queryExp = null;
- for (int i = 0; i < attributes.length; i++) {
- if (values[i] instanceof Boolean) {
- if (queryExp == null) {
- queryExp = Query.eq(Query.attr(attributes[i]), Query.value(((Boolean) values[i])));
- } else {
- queryExp = Query.and(queryExp,
- Query.eq(Query.attr(attributes[i]), Query.value(((Boolean) values[i]))));
- }
- } else if (values[i] instanceof Number) {
- if (queryExp == null) {
- queryExp = Query.eq(Query.attr(attributes[i]), Query.value((Number) values[i]));
- } else {
- queryExp = Query.and(queryExp,
- Query.eq(Query.attr(attributes[i]), Query.value((Number) values[i])));
- }
- } else if (values[i] instanceof String) {
- if (queryExp == null) {
- queryExp = Query.eq(Query.attr(attributes[i]), Query.value((String) values[i]));
- } else {
- queryExp = Query.and(queryExp,
- Query.eq(Query.attr(attributes[i]), Query.value((String) values[i])));
- }
- }
- }
- return queryExp;
- }
-
- /**
- * Invokes an operation on the specified MBean.
- *
- * @param objectName identifies the MBean
- * @param method the name of the operation method invoke
- *
- * @return the result of invoking the operation on the MBean specified or null
- *
- * @throws InstanceNotFoundException if the specified MBean is not registered in the MBean server
- * @throws IOException if a communication problem occurred when talking to the MBean server
- * @throws MBeanException if the MBean operation throws an exception
- * @throws ReflectionException if the MBean does not have the specified operation
- */
- private Object invoke(final ObjectName objectName, final String method)
- throws InstanceNotFoundException, IOException, MBeanException, ReflectionException {
- return this.server.invoke(objectName, method, new Object[] {}, new String[] {});
- }
-
- /**
- * Reads in the pid from the specified file.
- *
- * @param pidFile the file containing the pid of the process to stop
- *
- * @return the process id (pid) contained within the pidFile
- *
- * @throws IllegalArgumentException if the pid in the pidFile is not a positive integer
- * @throws IOException if unable to read from the specified file
- * @throws NumberFormatException if the pid file does not contain a parsable integer
- */
- private static int readPid(final File pidFile) throws IOException {
- BufferedReader fileReader = null;
- String pidValue = null;
-
- try {
- fileReader = new BufferedReader(new FileReader(pidFile));
- pidValue = fileReader.readLine();
-
- final int pid = Integer.parseInt(pidValue);
-
- if (pid < 1) {
- throw new IllegalArgumentException("Invalid pid '" + pid + "' found in " + pidFile);
- }
-
- return pid;
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("Invalid pid '" + pidValue + "' found in " + pidFile);
- } finally {
- IOUtils.close(fileReader);
- }
- }
-
- /**
- * Reads in the pid from the named file contained within the specified directory.
- *
- * @param directory directory containing a file of name pidFileName
- * @param pidFilename name of the file containing the pid of the process to stop
- *
- * @return the process id (pid) contained within the pidFile
- *
- * @throws FileNotFoundException if the specified file name is not found within the directory
- * @throws IllegalArgumentException if the pid in the pidFile is not a positive integer
- * @throws IllegalStateException if dir is not an existing directory
- * @throws IOException if an I/O error occurs
- * @throws NumberFormatException if the pid file does not contain a parsable integer
- */
- private static int readPid(final File directory, final String pidFilename) throws IOException {
- if (!directory.isDirectory() && directory.exists()) {
- throw new IllegalArgumentException(
- "Argument '" + directory + "' must be an existing directory!");
- }
-
- final File[] files = directory.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File file, String filename) {
- return filename.equals(pidFilename);
- }
- });
-
- if (files.length == 0) {
- throw new FileNotFoundException(
- "Unable to find PID file '" + pidFilename + "' in directory " + directory);
- }
-
- return readPid(files[0]);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessLauncher.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessLauncher.java b/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessLauncher.java
index 04809c2..598a75e 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessLauncher.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/LocalProcessLauncher.java
@@ -14,16 +14,21 @@
*/
package org.apache.geode.internal.process;
-import org.apache.geode.distributed.internal.DistributionConfig;
+import static org.apache.commons.lang.Validate.notNull;
+import static org.apache.geode.internal.process.ProcessUtils.identifyPid;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import org.apache.geode.distributed.internal.DistributionConfig;
+
/**
* Creates a pid file and writes the process id to the pid file.
- * <p/>
+ *
+ * <p>
* Related articles and libraries:
+ *
* <ul>
* <li>http://barelyenough.org/blog/2005/03/java-daemon/
* <li>http://stackoverflow.com/questions/534648/how-to-daemonize-a-java-program
@@ -31,12 +36,13 @@ import java.io.IOException;
* <li>http://wrapper.tanukisoftware.com/
* <li>http://weblogs.java.net/blog/kohsuke/archive/2009/01/writing_a_unix.html
* <li>http://www.enderunix.org/docs/eng/daemon.php
+ * </ul>
*
* @since GemFire 7.0
*/
-public class LocalProcessLauncher {
+class LocalProcessLauncher {
- public static final String PROPERTY_IGNORE_IS_PID_ALIVE =
+ static final String PROPERTY_IGNORE_IS_PID_ALIVE =
DistributionConfig.GEMFIRE_PREFIX + "test.LocalProcessLauncher.ignoreIsPidAlive";
private final int pid;
@@ -55,9 +61,11 @@ public class LocalProcessLauncher {
*
* @see java.lang.management.RuntimeMXBean
*/
- public LocalProcessLauncher(final File pidFile, final boolean force)
+ LocalProcessLauncher(final File pidFile, final boolean force)
throws FileAlreadyExistsException, IOException, PidUnavailableException {
- this.pid = ProcessUtils.identifyPid();
+ notNull(pidFile, "Invalid pidFile '" + pidFile + "' specified");
+
+ this.pid = identifyPid();
this.pidFile = pidFile;
writePid(force);
}
@@ -67,8 +75,8 @@ public class LocalProcessLauncher {
*
* @return the process id (pid)
*/
- public int getPid() {
- return this.pid;
+ int getPid() {
+ return pid;
}
/**
@@ -76,15 +84,28 @@ public class LocalProcessLauncher {
*
* @return the pid file
*/
- public File getPidFile() {
- return this.pidFile;
+ File getPidFile() {
+ return pidFile;
}
/**
* Delete the pid file now. {@link java.io.File#deleteOnExit()} is set on the pid file.
+ *
*/
void close() {
- this.pidFile.delete();
+ pidFile.delete();
+ }
+
+ /**
+ * Delete the pid file now. {@link java.io.File#deleteOnExit()} is set on the pid file.
+ *
+ * @param deletePidFileOnClose if true then the pid file will be deleted now instead of during JVM
+ * exit
+ */
+ void close(final boolean deletePidFileOnClose) {
+ if (deletePidFileOnClose) {
+ pidFile.delete();
+ }
}
/**
@@ -96,30 +117,44 @@ public class LocalProcessLauncher {
* @throws IOException if unable to create or write to the file
*/
private void writePid(final boolean force) throws FileAlreadyExistsException, IOException {
- final boolean created = this.pidFile.createNewFile();
- if (!created && !force) {
- int otherPid = 0;
- try {
- otherPid = ProcessUtils.readPid(this.pidFile);
- } catch (IOException e) {
- // suppress
- } catch (NumberFormatException e) {
- // suppress
- }
- boolean ignorePidFile = false;
- if (otherPid != 0 && !ignoreIsPidAlive()) {
- ignorePidFile = !ProcessUtils.isProcessAlive(otherPid);
- }
- if (!ignorePidFile) {
- throw new FileAlreadyExistsException("Pid file already exists: " + this.pidFile + " for "
- + (otherPid > 0 ? "process " + otherPid : "unknown process"));
+ if (pidFile.exists()) {
+ if (!force) {
+ checkOtherPid(readOtherPid());
}
+ pidFile.delete();
+ }
+
+ File tempPidFile = new File(pidFile.getParent(), pidFile.getName() + ".tmp");
+ tempPidFile.createNewFile();
+
+ try (FileWriter writer = new FileWriter(tempPidFile)) {
+ writer.write(String.valueOf(pid));
+ writer.flush();
}
- this.pidFile.deleteOnExit();
- final FileWriter writer = new FileWriter(this.pidFile);
- writer.write(String.valueOf(this.pid));
- writer.flush();
- writer.close();
+
+ tempPidFile.renameTo(pidFile);
+ pidFile.deleteOnExit();
+ }
+
+ private int readOtherPid() {
+ int otherPid = 0;
+ try {
+ otherPid = ProcessUtils.readPid(pidFile);
+ } catch (NumberFormatException | IOException ignore) {
+ // suppress
+ }
+ return otherPid;
+ }
+
+ private void checkOtherPid(final int otherPid) throws FileAlreadyExistsException {
+ if (ignoreIsPidAlive() || otherPid != 0 && isProcessAlive(otherPid)) {
+ throw new FileAlreadyExistsException("Pid file already exists: " + pidFile + " for "
+ + (otherPid > 0 ? "process " + otherPid : "unknown process"));
+ }
+ }
+
+ private boolean isProcessAlive(final int pid) {
+ return ignoreIsPidAlive() || ProcessUtils.isProcessAlive(pid);
}
private static boolean ignoreIsPidAlive() {
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/MBeanControllerParameters.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/MBeanControllerParameters.java b/geode-core/src/main/java/org/apache/geode/internal/process/MBeanControllerParameters.java
index 857c52d..2ea0fa4 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/MBeanControllerParameters.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/MBeanControllerParameters.java
@@ -25,15 +25,16 @@ import org.apache.geode.internal.process.ProcessController.Arguments;
* @since GemFire 8.0
*/
interface MBeanControllerParameters extends Arguments {
- public ObjectName getNamePattern();
- public String getPidAttribute();
+ ObjectName getNamePattern();
- public String getStatusMethod();
+ String getPidAttribute();
- public String getStopMethod();
+ String getStatusMethod();
- public String[] getAttributes();
+ String getStopMethod();
- public Object[] getValues();
+ String[] getAttributes();
+
+ Object[] getValues();
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/MBeanInvocationFailedException.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/MBeanInvocationFailedException.java b/geode-core/src/main/java/org/apache/geode/internal/process/MBeanInvocationFailedException.java
index 724a4d7..d4ba3ec 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/MBeanInvocationFailedException.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/MBeanInvocationFailedException.java
@@ -23,23 +23,23 @@ public class MBeanInvocationFailedException extends Exception {
private static final long serialVersionUID = 7991096466859690801L;
/**
- * Creates a new <code>MBeanInvocationFailedException</code>.
+ * Creates a new {@code MBeanInvocationFailedException}.
*/
public MBeanInvocationFailedException(final String message) {
super(message);
}
/**
- * Creates a new <code>MBeanInvocationFailedException</code> that was caused by a given exception
+ * Creates a new {@code MBeanInvocationFailedException} that was caused by a given exception
*/
- public MBeanInvocationFailedException(final String message, final Throwable thr) {
- super(message, thr);
+ public MBeanInvocationFailedException(final String message, final Throwable cause) {
+ super(message, cause);
}
/**
- * Creates a new <code>MBeanInvocationFailedException</code> that was caused by a given exception
+ * Creates a new {@code MBeanInvocationFailedException} that was caused by a given exception
*/
- public MBeanInvocationFailedException(final Throwable thr) {
- super(thr.getMessage(), thr);
+ public MBeanInvocationFailedException(final Throwable cause) {
+ super(cause.getMessage(), cause);
}
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/MBeanProcessController.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/MBeanProcessController.java b/geode-core/src/main/java/org/apache/geode/internal/process/MBeanProcessController.java
index ea5946e..1a19719 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/MBeanProcessController.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/MBeanProcessController.java
@@ -14,10 +14,11 @@
*/
package org.apache.geode.internal.process;
+import static org.apache.commons.lang.Validate.isTrue;
+import static org.apache.commons.lang.Validate.notNull;
+
import java.io.File;
import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.lang.management.RuntimeMXBean;
import java.util.Properties;
import java.util.Set;
@@ -42,17 +43,20 @@ import com.sun.tools.attach.VirtualMachine;
*
* @since GemFire 8.0
*/
-public class MBeanProcessController implements ProcessController {
+class MBeanProcessController implements ProcessController {
/** Property name for the JMX local connector address (from sun.management.Agent) */
- private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
+ private static final String PROPERTY_LOCAL_CONNECTOR_ADDRESS =
"com.sun.management.jmxremote.localConnectorAddress";
+ private static final Object[] PARAMS = {};
+ private static final String[] SIGNATURE = {};
+
private final MBeanControllerParameters arguments;
private final int pid;
- protected JMXConnector jmxc;
- protected MBeanServerConnection server;
+ private JMXConnector jmxc;
+ private MBeanServerConnection server;
/**
* Constructs an instance for controlling a local process.
@@ -61,36 +65,37 @@ public class MBeanProcessController implements ProcessController {
*
* @throws IllegalArgumentException if pid is not a positive integer
*/
- public MBeanProcessController(final MBeanControllerParameters arguments, final int pid) {
- if (pid < 1) {
- throw new IllegalArgumentException("Invalid pid '" + pid + "' specified");
- }
+ MBeanProcessController(final MBeanControllerParameters arguments, final int pid) {
+ notNull(arguments, "Invalid arguments '" + arguments + "' specified");
+ isTrue(pid > 0, "Invalid pid '" + pid + "' specified");
+
this.pid = pid;
this.arguments = arguments;
}
@Override
public int getProcessId() {
- return this.pid;
+ return pid;
}
@Override
public String status() throws UnableToControlProcessException, ConnectionFailedException,
IOException, MBeanInvocationFailedException {
- return status(this.arguments.getNamePattern(), this.arguments.getPidAttribute(),
- this.arguments.getStatusMethod(), this.arguments.getAttributes(),
- this.arguments.getValues());
+ return status(arguments.getNamePattern(), arguments.getPidAttribute(),
+ arguments.getStatusMethod(), arguments.getAttributes(), arguments.getValues());
}
@Override
public void stop() throws UnableToControlProcessException, ConnectionFailedException, IOException,
MBeanInvocationFailedException {
- stop(this.arguments.getNamePattern(), this.arguments.getPidAttribute(),
- this.arguments.getStopMethod(), this.arguments.getAttributes(), this.arguments.getValues());
+ stop(arguments.getNamePattern(), arguments.getPidAttribute(), arguments.getStopMethod(),
+ arguments.getAttributes(), arguments.getValues());
}
@Override
- public void checkPidSupport() {}
+ public void checkPidSupport() {
+ // nothing
+ }
/**
* Connects to the process and tells it to shut down.
@@ -156,27 +161,21 @@ public class MBeanProcessController implements ProcessController {
ObjectName objectName = namePattern;
connect();
try {
- final QueryExp constraint = buildQueryExp(pidAttribute, attributes, values);
- final Set<ObjectName> mbeanNames = this.server.queryNames(namePattern, constraint);
+ QueryExp constraint = buildQueryExp(pidAttribute, attributes, values);
+ Set<ObjectName> mbeanNames = server.queryNames(namePattern, constraint);
if (mbeanNames.isEmpty()) {
throw new MBeanInvocationFailedException("Failed to find mbean matching '" + namePattern
- + "' with attribute '" + pidAttribute + "' of value '" + this.pid + "'");
+ + "' with attribute '" + pidAttribute + "' of value '" + pid + "'");
}
if (mbeanNames.size() > 1) {
throw new MBeanInvocationFailedException("Found more than one mbean matching '"
- + namePattern + "' with attribute '" + pidAttribute + "' of value '" + this.pid + "'");
+ + namePattern + "' with attribute '" + pidAttribute + "' of value '" + pid + "'");
}
objectName = mbeanNames.iterator().next();
return invoke(objectName, methodName);
- } catch (InstanceNotFoundException e) {
- throw new MBeanInvocationFailedException(
- "Failed to invoke " + methodName + " on " + objectName, e);
- } catch (MBeanException e) {
- throw new MBeanInvocationFailedException(
- "Failed to invoke " + methodName + " on " + objectName, e);
- } catch (ReflectionException e) {
+ } catch (InstanceNotFoundException | MBeanException | ReflectionException e) {
throw new MBeanInvocationFailedException(
"Failed to invoke " + methodName + " on " + objectName, e);
} finally {
@@ -193,11 +192,11 @@ public class MBeanProcessController implements ProcessController {
*/
private void connect() throws ConnectionFailedException, IOException {
try {
- final JMXServiceURL jmxUrl = getJMXServiceURL();
- this.jmxc = JMXConnectorFactory.connect(jmxUrl);
- this.server = this.jmxc.getMBeanServerConnection();
+ JMXServiceURL jmxUrl = getJMXServiceURL();
+ jmxc = JMXConnectorFactory.connect(jmxUrl);
+ server = jmxc.getMBeanServerConnection();
} catch (AttachNotSupportedException e) {
- throw new ConnectionFailedException("Failed to connect to process '" + this.pid + "'", e);
+ throw new ConnectionFailedException("Failed to connect to process '" + pid + "'", e);
}
}
@@ -205,38 +204,15 @@ public class MBeanProcessController implements ProcessController {
* Disconnects from the JMX agent in the local process.
*/
private void disconnect() {
- this.server = null;
- if (this.jmxc != null) {
+ server = null;
+ if (jmxc != null) {
try {
- this.jmxc.close();
- } catch (IOException e) {
+ jmxc.close();
+ } catch (IOException ignored) {
// ignore
}
}
- this.jmxc = null;
- }
-
- /**
- * Ensures that the other process identifies itself by the same pid used by this stopper to
- * connect to that process. NOT USED EXCEPT IN TEST.
- *
- * @return true if the pid matches
- *
- * @throws IllegalStateException if the other process identifies itself by a different pid
- * @throws IOException if a communication problem occurred when accessing the
- * MBeanServerConnection
- * @throws PidUnavailableException if parsing the pid from the RuntimeMXBean name fails
- */
- boolean checkPidMatches() throws IllegalStateException, IOException, PidUnavailableException {
- final RuntimeMXBean proxy = ManagementFactory.newPlatformMXBeanProxy(this.server,
- ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class);
- final int remotePid = ProcessUtils.identifyPid(proxy.getName());
- if (remotePid != this.pid) {
- throw new IllegalStateException(
- "Process has different pid '" + remotePid + "' than expected pid '" + this.pid + "'");
- } else {
- return true;
- }
+ jmxc = null;
}
/**
@@ -250,16 +226,16 @@ public class MBeanProcessController implements ProcessController {
* @throws IOException if the JDK management agent cannot be found and loaded
*/
private JMXServiceURL getJMXServiceURL() throws AttachNotSupportedException, IOException {
- String connectorAddress = null;
- final VirtualMachine vm = VirtualMachine.attach(String.valueOf(this.pid));
+ String connectorAddress;
+ VirtualMachine vm = VirtualMachine.attach(String.valueOf(pid));
try {
Properties agentProps = vm.getAgentProperties();
- connectorAddress = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
+ connectorAddress = agentProps.getProperty(PROPERTY_LOCAL_CONNECTOR_ADDRESS);
if (connectorAddress == null) {
// need to load the management-agent and get the address
- final String javaHome = vm.getSystemProperties().getProperty("java.home");
+ String javaHome = vm.getSystemProperties().getProperty("java.home");
// assume java.home is JDK and look in JRE for agent
String managementAgentPath = javaHome + File.separator + "jre" + File.separator + "lib"
@@ -279,19 +255,13 @@ public class MBeanProcessController implements ProcessController {
managementAgentPath = managementAgent.getCanonicalPath();
try {
vm.loadAgent(managementAgentPath, "com.sun.management.jmxremote");
- } catch (AgentLoadException e) {
- IOException ioe = new IOException(e.getMessage());
- ioe.initCause(e);
- throw ioe;
- } catch (AgentInitializationException e) {
- IOException ioe = new IOException(e.getMessage());
- ioe.initCause(e);
- throw ioe;
+ } catch (AgentLoadException | AgentInitializationException e) {
+ throw new IOException(e);
}
// get the connector address
agentProps = vm.getAgentProperties();
- connectorAddress = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
+ connectorAddress = agentProps.getProperty(PROPERTY_LOCAL_CONNECTOR_ADDRESS);
}
} finally {
vm.detach();
@@ -316,13 +286,13 @@ public class MBeanProcessController implements ProcessController {
*/
private QueryExp buildQueryExp(final String pidAttribute, final String[] attributes,
final Object[] values) {
- final QueryExp optionalAttributes = buildOptionalQueryExp(attributes, values);
+ QueryExp optionalAttributes = buildOptionalQueryExp(attributes, values);
QueryExp constraint;
if (optionalAttributes != null) {
constraint =
- Query.and(optionalAttributes, Query.eq(Query.attr(pidAttribute), Query.value(this.pid)));
+ Query.and(optionalAttributes, Query.eq(Query.attr(pidAttribute), Query.value(pid)));
} else {
- constraint = Query.eq(Query.attr(pidAttribute), Query.value(this.pid));
+ constraint = Query.eq(Query.attr(pidAttribute), Query.value(pid));
}
return constraint;
}
@@ -342,10 +312,10 @@ public class MBeanProcessController implements ProcessController {
for (int i = 0; i < attributes.length; i++) {
if (values[i] instanceof Boolean) {
if (queryExp == null) {
- queryExp = Query.eq(Query.attr(attributes[i]), Query.value(((Boolean) values[i])));
+ queryExp = Query.eq(Query.attr(attributes[i]), Query.value((Boolean) values[i]));
} else {
queryExp = Query.and(queryExp,
- Query.eq(Query.attr(attributes[i]), Query.value(((Boolean) values[i]))));
+ Query.eq(Query.attr(attributes[i]), Query.value((Boolean) values[i])));
}
} else if (values[i] instanceof Number) {
if (queryExp == null) {
@@ -381,6 +351,6 @@ public class MBeanProcessController implements ProcessController {
*/
private Object invoke(final ObjectName objectName, final String method)
throws InstanceNotFoundException, IOException, MBeanException, ReflectionException {
- return this.server.invoke(objectName, method, new Object[] {}, new String[] {});
+ return server.invoke(objectName, method, PARAMS, SIGNATURE);
}
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/NativeProcessUtils.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/NativeProcessUtils.java b/geode-core/src/main/java/org/apache/geode/internal/process/NativeProcessUtils.java
index 34cf81f..cea73de 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/NativeProcessUtils.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/NativeProcessUtils.java
@@ -14,6 +14,8 @@
*/
package org.apache.geode.internal.process;
+import static org.apache.commons.lang.Validate.isTrue;
+
import org.apache.geode.internal.process.ProcessUtils.InternalProcessUtils;
import org.apache.geode.internal.shared.NativeCalls;
@@ -24,17 +26,19 @@ import org.apache.geode.internal.shared.NativeCalls;
*/
class NativeProcessUtils implements InternalProcessUtils {
- private final static NativeCalls nativeCalls = NativeCalls.getInstance();
-
- NativeProcessUtils() {}
+ private static final NativeCalls nativeCalls = NativeCalls.getInstance();
@Override
- public boolean isProcessAlive(int pid) {
+ public boolean isProcessAlive(final int pid) {
+ isTrue(pid > 0, "Invalid pid '" + pid + "' specified");
+
return nativeCalls.isProcessActive(pid);
}
@Override
- public boolean killProcess(int pid) {
+ public boolean killProcess(final int pid) {
+ isTrue(pid > 0, "Invalid pid '" + pid + "' specified");
+
return nativeCalls.killProcess(pid);
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/NonBlockingProcessStreamReader.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/NonBlockingProcessStreamReader.java b/geode-core/src/main/java/org/apache/geode/internal/process/NonBlockingProcessStreamReader.java
index d5c1116..9096fae 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/NonBlockingProcessStreamReader.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/NonBlockingProcessStreamReader.java
@@ -30,7 +30,7 @@ import org.apache.geode.internal.util.StopWatch;
*
* @since GemFire 8.2
*/
-public class NonBlockingProcessStreamReader extends ProcessStreamReader {
+class NonBlockingProcessStreamReader extends ProcessStreamReader {
private static final Logger logger = LogService.getLogger();
/**
@@ -39,61 +39,65 @@ public class NonBlockingProcessStreamReader extends ProcessStreamReader {
*/
private final long continueReadingMillis;
- protected NonBlockingProcessStreamReader(final Builder builder) {
+ private final StopWatch continueReading;
+
+ private StringBuilder stringBuilder;
+ private int character;
+ private boolean ready;
+
+ NonBlockingProcessStreamReader(final Builder builder) {
super(builder);
- continueReadingMillis = builder.continueReadingMillis;
+
+ this.continueReadingMillis = builder.continueReadingMillis;
+ this.continueReading = new StopWatch();
+ this.stringBuilder = new StringBuilder();
+ this.character = 0;
+ this.ready = false;
}
@Override
public void run() {
- final boolean isDebugEnabled = logger.isDebugEnabled();
- if (isDebugEnabled) {
- logger.debug("Running {}", this);
- }
- StopWatch continueReading = new StopWatch();
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(new InputStreamReader(inputStream));
- StringBuilder sb = new StringBuilder();
- boolean ready = false;
- int ch = 0;
- while (ch != -1) {
- while ((ready = reader.ready()) && (ch = reader.read()) != -1) {
- sb.append((char) ch);
- if ((char) ch == '\n') {
- this.inputListener.notifyInputLine(sb.toString());
- sb = new StringBuilder();
- }
- }
- if (!ready) {
- if (!ProcessUtils.isProcessAlive(process)) {
- if (!continueReading.isRunning()) {
- continueReading.start();
- } else if (continueReading.elapsedTimeMillis() > continueReadingMillis) {
- return;
- }
- }
- Thread.sleep(10);
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ while (character != -1) {
+ readWhileReady(reader);
+ if (shouldTerminate()) {
+ break;
}
}
} catch (IOException e) {
- if (isDebugEnabled) {
+ if (logger.isDebugEnabled()) {
logger.debug("Failure reading from buffered input stream: {}", e.getMessage(), e);
}
} catch (InterruptedException e) {
- if (isDebugEnabled) {
+ if (logger.isDebugEnabled()) {
logger.debug("Interrupted reading from buffered input stream: {}", e.getMessage(), e);
}
- } finally {
- try {
- reader.close();
- } catch (IOException e) {
- if (isDebugEnabled) {
- logger.debug("Failure closing buffered input stream reader: {}", e.getMessage(), e);
- }
+ }
+ }
+
+ private boolean shouldTerminate() throws InterruptedException {
+ if (!ProcessUtils.isProcessAlive(process)) {
+ if (!continueReading.isRunning()) {
+ continueReading.start();
+ } else if (continueReading.elapsedTimeMillis() > continueReadingMillis) {
+ return true;
}
- if (isDebugEnabled) {
- logger.debug("Terminating {}", this);
+ }
+ Thread.sleep(10);
+ return false;
+ }
+
+ /**
+ * This is a hot reader while there are characters ready to read. As soon as there are no more
+ * characters to read, it returns and the loop invokes shouldTerminate which has a 10 millisecond
+ * sleep until there are more characters ready to read.
+ */
+ private void readWhileReady(BufferedReader reader) throws IOException {
+ while ((ready = reader.ready()) && (character = reader.read()) != -1) {
+ stringBuilder.append((char) character);
+ if ((char) character == '\n') {
+ this.inputListener.notifyInputLine(stringBuilder.toString());
+ stringBuilder = new StringBuilder();
}
}
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/PidFile.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/PidFile.java b/geode-core/src/main/java/org/apache/geode/internal/process/PidFile.java
index 291c202..0e7adf2 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/PidFile.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/PidFile.java
@@ -14,17 +14,15 @@
*/
package org.apache.geode.internal.process;
+import static org.apache.commons.lang.Validate.isTrue;
+import static org.apache.commons.lang.Validate.notEmpty;
+import static org.apache.commons.lang.Validate.notNull;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
-import java.io.FilenameFilter;
import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.geode.internal.util.IOUtils;
-import org.apache.geode.internal.util.StopWatch;
/**
* File wrapper that adds support for reading process id (pid) from a pid file written to disk by
@@ -34,8 +32,6 @@ import org.apache.geode.internal.util.StopWatch;
*/
public class PidFile {
- private static final long SLEEP_INTERVAL_MILLIS = 10;
-
private final File pidFile;
/**
@@ -43,17 +39,13 @@ public class PidFile {
*
* @param file the file containing the pid of the process
*
- * @throws FileNotFoundException if the specified file name is not found within the directory
+ * @throws IllegalArgumentException if the specified file is null or does not exist
*/
- public PidFile(final File file) throws FileNotFoundException {
- if (!file.exists() || !file.isFile()) {
- throw new FileNotFoundException("Unable to find PID file '" + file + "'");
- }
- this.pidFile = file;
- }
+ public PidFile(final File file) {
+ notNull(file, "Invalid file '" + file + "' specified");
+ isTrue(file.exists(), "Nonexistent file '" + file + "' specified");
- File getFile() {
- return this.pidFile;
+ this.pidFile = file;
}
/**
@@ -62,19 +54,19 @@ public class PidFile {
* @param directory directory containing a file of name pidFileName
* @param filename name of the file containing the pid of the process to stop
*
- * @throws FileNotFoundException if the specified file name is not found within the directory
- * @throws IllegalStateException if dir is not an existing directory
+ * @throws FileNotFoundException if the specified filename is not found within the directory
+ * @throws IllegalArgumentException if directory is null, does not exist or is not a directory
*/
public PidFile(final File directory, final String filename) throws FileNotFoundException {
- if (!directory.isDirectory() && directory.exists()) {
- throw new IllegalArgumentException(
- "Argument '" + directory + "' must be an existing directory!");
- }
+ notNull(directory, "Invalid directory '" + directory + "' specified");
+ notEmpty(filename, "Invalid filename '" + filename + "' specified");
+ isTrue(directory.isDirectory() && directory.exists(),
+ "Nonexistent directory '" + directory + "' specified");
- final File file = new File(directory, filename);
+ File file = new File(directory, filename);
if (!file.exists() || file.isDirectory()) {
throw new FileNotFoundException(
- "Unable to find PID file '" + filename + "' in directory " + directory);
+ "Unable to find PID file '" + filename + "' in directory '" + directory + "'");
}
this.pidFile = file;
@@ -89,81 +81,26 @@ public class PidFile {
* @throws IOException if unable to read from the specified file
*/
public int readPid() throws IOException {
- BufferedReader fileReader = null;
String pidValue = null;
-
- try {
- fileReader = new BufferedReader(new FileReader(this.pidFile));
+ try (BufferedReader fileReader = new BufferedReader(new FileReader(pidFile))) {
pidValue = fileReader.readLine();
- final int pid = Integer.parseInt(pidValue);
+ int pid = Integer.parseInt(pidValue);
if (pid < 1) {
throw new IllegalArgumentException(
- "Invalid pid '" + pid + "' found in " + this.pidFile.getCanonicalPath());
+ "Invalid pid '" + pid + "' found in " + pidFile.getCanonicalPath());
}
return pid;
- } catch (NumberFormatException e) {
+ } catch (NumberFormatException ignored) {
throw new IllegalArgumentException(
- "Invalid pid '" + pidValue + "' found in " + this.pidFile.getCanonicalPath());
- } finally {
- IOUtils.close(fileReader);
+ "Invalid pid '" + pidValue + "' found in " + pidFile.getCanonicalPath());
}
}
- /**
- * Reads in the pid from the specified file, retrying until the specified timeout.
- *
- * @param timeout the maximum time to spend trying to read the pidFile
- * @param unit the unit of timeout
- *
- * @return the process id (pid) contained within the pidFile
- *
- * @throws IllegalArgumentException if the pid in the pidFile is not a positive integer
- * @throws IOException if unable to read from the specified file
- * @throws InterruptedException if interrupted
- * @throws TimeoutException if operation times out
- */
- public int readPid(final long timeout, final TimeUnit unit)
- throws IOException, InterruptedException, TimeoutException {
- IllegalArgumentException iae = null;
- IOException ioe = null;
- int pid = 0;
-
- final long timeoutMillis = unit.toMillis(timeout);
- final StopWatch stopWatch = new StopWatch(true);
-
- while (pid <= 0) {
- try {
- pid = readPid();
- } catch (IllegalArgumentException e) {
- iae = e;
- } catch (IOException e) {
- ioe = e;
- }
- if (stopWatch.elapsedTimeMillis() > timeoutMillis) {
- if (iae != null) {
- throw new TimeoutException(iae.getMessage());
- }
- if (ioe != null) {
- throw new TimeoutException(ioe.getMessage());
- }
- } else {
- try {
- Thread.sleep(SLEEP_INTERVAL_MILLIS);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- if (iae != null) {
- throw new InterruptedException(iae.getMessage());
- }
- if (ioe != null) {
- throw new InterruptedException(ioe.getMessage());
- }
- throw e;
- }
- }
- }
- return pid;
+ File getFile() {
+ return pidFile;
}
+
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/PidUnavailableException.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/PidUnavailableException.java b/geode-core/src/main/java/org/apache/geode/internal/process/PidUnavailableException.java
index 6fa1c9f..934e65c 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/PidUnavailableException.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/PidUnavailableException.java
@@ -24,23 +24,23 @@ public class PidUnavailableException extends Exception {
private static final long serialVersionUID = -1660269538268828059L;
/**
- * Creates a new <code>PidUnavailableException</code>.
+ * Creates a new {@code PidUnavailableException}.
*/
public PidUnavailableException(final String message) {
super(message);
}
/**
- * Creates a new <code>PidUnavailableException</code> that was caused by a given exception
+ * Creates a new {@code PidUnavailableException} that was caused by a given exception
*/
- public PidUnavailableException(final String message, final Throwable thr) {
- super(message, thr);
+ public PidUnavailableException(final String message, final Throwable cause) {
+ super(message, cause);
}
/**
- * Creates a new <code>PidUnavailableException</code> that was caused by a given exception
+ * Creates a new {@code PidUnavailableException} that was caused by a given exception
*/
- public PidUnavailableException(final Throwable thr) {
- super(thr.getMessage(), thr);
+ public PidUnavailableException(final Throwable cause) {
+ super(cause.getMessage(), cause);
}
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/ProcessController.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/ProcessController.java b/geode-core/src/main/java/org/apache/geode/internal/process/ProcessController.java
index 2aa4732..2e5cb0c 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/ProcessController.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/ProcessController.java
@@ -27,19 +27,19 @@ public interface ProcessController {
/**
* Returns the status of a running GemFire {@link ControllableProcess}.
*/
- public String status() throws UnableToControlProcessException, ConnectionFailedException,
- IOException, MBeanInvocationFailedException, InterruptedException, TimeoutException;
+ String status() throws UnableToControlProcessException, ConnectionFailedException, IOException,
+ MBeanInvocationFailedException, InterruptedException, TimeoutException;
/**
* Stops a running GemFire {@link ControllableProcess}.
*/
- public void stop() throws UnableToControlProcessException, ConnectionFailedException, IOException,
+ void stop() throws UnableToControlProcessException, ConnectionFailedException, IOException,
MBeanInvocationFailedException;
/**
* Returns the PID of a running GemFire {@link ControllableProcess}.
*/
- public int getProcessId();
+ int getProcessId();
/**
* Checks if {@link #status} and {@link #stop} are supported if only the PID is provided. Only the
@@ -48,14 +48,15 @@ public interface ProcessController {
*
* @throws org.apache.geode.lang.AttachAPINotFoundException if the Attach API is not found
*/
- public void checkPidSupport();
+ void checkPidSupport();
/**
* Defines the arguments that a client must provide to the ProcessController.
*/
- static interface Arguments {
- public int getProcessId();
+ interface Arguments {
- public ProcessType getProcessType();
+ int getProcessId();
+
+ ProcessType getProcessType();
}
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerFactory.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerFactory.java b/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerFactory.java
index 38fdcf8..ef276c3 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerFactory.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerFactory.java
@@ -14,13 +14,16 @@
*/
package org.apache.geode.internal.process;
-import org.apache.geode.distributed.internal.DistributionConfig;
+import static org.apache.commons.lang.Validate.isTrue;
+import static org.apache.commons.lang.Validate.notEmpty;
+import static org.apache.commons.lang.Validate.notNull;
import java.io.File;
import java.io.IOException;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import org.apache.geode.distributed.internal.DistributionConfig;
+
/**
* Manages which implementation of {@link ProcessController} will be used and constructs the
* instance.
@@ -29,6 +32,9 @@ import java.util.concurrent.TimeoutException;
*/
public class ProcessControllerFactory {
+ /**
+ * For testing only
+ */
public static final String PROPERTY_DISABLE_ATTACH_API =
DistributionConfig.GEMFIRE_PREFIX + "test.ProcessControllerFactory.DisableAttachApi";
@@ -38,64 +44,44 @@ public class ProcessControllerFactory {
this.disableAttachApi = Boolean.getBoolean(PROPERTY_DISABLE_ATTACH_API);
}
- public ProcessController createProcessController(final ProcessControllerParameters arguments,
+ public ProcessController createProcessController(final ProcessControllerParameters parameters,
final int pid) {
- if (arguments == null) {
- throw new NullPointerException("ProcessControllerParameters must not be null");
- }
- if (pid < 1) {
- throw new IllegalArgumentException("Invalid pid '" + pid + "' specified");
- }
- try {
- if (isAttachAPIFound()) {
- return new MBeanProcessController((MBeanControllerParameters) arguments, pid);
- } else {
- return new FileProcessController((FileControllerParameters) arguments, pid);
+ notNull(parameters, "Invalid parameters '" + parameters + "' specified");
+ isTrue(pid > 0, "Invalid pid '" + pid + "' specified");
+
+ if (isAttachAPIFound()) {
+ try {
+ return new MBeanProcessController(parameters, pid);
+ } catch (ExceptionInInitializerError ignore) {
}
- } catch (final ExceptionInInitializerError e) {
- // LOGGER.warn("Attach API class not found", e);
}
- return null;
+ return new FileProcessController(parameters, pid);
}
- public ProcessController createProcessController(final ProcessControllerParameters arguments,
- final File pidFile, final long timeout, final TimeUnit unit)
+ public ProcessController createProcessController(final ProcessControllerParameters parameters,
+ final File directory, final String pidFileName)
throws IOException, InterruptedException, TimeoutException {
- if (arguments == null) {
- throw new NullPointerException("ProcessControllerParameters must not be null");
- }
- if (pidFile == null) {
- throw new NullPointerException("Pid file must not be null");
- }
- return createProcessController(arguments, new PidFile(pidFile).readPid(timeout, unit));
- }
+ notNull(parameters, "Invalid parameters '" + parameters + "' specified");
+ notNull(directory, "Invalid directory '" + directory + "' specified");
+ notEmpty(pidFileName, "Invalid pidFileName '" + pidFileName + "' specified");
- public ProcessController createProcessController(final ProcessControllerParameters arguments,
- final File directory, final String pidFilename, final long timeout, final TimeUnit unit)
- throws IOException, InterruptedException, TimeoutException {
- if (arguments == null) {
- throw new NullPointerException("ProcessControllerParameters must not be null");
- }
- if (directory == null) {
- throw new NullPointerException("Directory must not be null");
- }
- if (pidFilename == null) {
- throw new NullPointerException("Pid file name must not be null");
- }
- return createProcessController(arguments,
- new PidFile(directory, pidFilename).readPid(timeout, unit));
+ return createProcessController(parameters, readPid(directory, pidFileName));
}
public boolean isAttachAPIFound() {
- if (this.disableAttachApi) {
+ if (disableAttachApi) {
return false;
}
boolean found = false;
try {
final Class<?> virtualMachineClass = Class.forName("com.sun.tools.attach.VirtualMachine");
found = virtualMachineClass != null;
- } catch (ClassNotFoundException e) {
+ } catch (ClassNotFoundException ignore) {
}
return found;
}
+
+ private int readPid(final File directory, final String pidFileName) throws IOException {
+ return new PidFile(directory, pidFileName).readPid();
+ }
}
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerParameters.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerParameters.java b/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerParameters.java
index eb3deb9..a20faf1 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerParameters.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/ProcessControllerParameters.java
@@ -15,10 +15,10 @@
package org.apache.geode.internal.process;
/**
- * Defines the methods for providing input arguments to the <code>ProcessController</code>.
+ * Defines the methods for providing input arguments to the {@code ProcessController}.
*
- * Implementations of <code>ProcessController</code> are in this package. Classes that implement
- * <code>ProcessControllerArguments</code> would typically be in a different package.
+ * Implementations of {@code ProcessController} are in this package. Classes that implement
+ * {@code ProcessController} would typically be in a different package.
*
* @since GemFire 8.0
*/
http://git-wip-us.apache.org/repos/asf/geode/blob/894f3ee7/geode-core/src/main/java/org/apache/geode/internal/process/ProcessLauncherContext.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/process/ProcessLauncherContext.java b/geode-core/src/main/java/org/apache/geode/internal/process/ProcessLauncherContext.java
index 463fd18..9b10550 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/process/ProcessLauncherContext.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/process/ProcessLauncherContext.java
@@ -12,16 +12,14 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-
package org.apache.geode.internal.process;
-import org.apache.geode.distributed.internal.DistributionConfig;
-import org.apache.geode.internal.io.TeeOutputStream;
-import org.apache.geode.internal.io.TeePrintStream;
+import static org.apache.commons.lang.Validate.notNull;
-import java.io.*;
import java.util.Properties;
+import org.apache.geode.distributed.internal.DistributionConfig;
+
/**
* Thread based context for launching a process. GemFire internals can acquire optional
* configuration details from a process launcher via this context.
@@ -43,8 +41,11 @@ public class ProcessLauncherContext {
*/
private static final Properties OVERRIDDEN_DEFAULTS_DEFAULT = new Properties();
- private static final ThreadLocal<ProcessLauncherContext> DATA =
- new ThreadLocal<ProcessLauncherContext>();
+ private static final ThreadLocal<ProcessLauncherContext> DATA = new ThreadLocal<>();
+
+ private final boolean redirectOutput;
+ private final Properties overriddenDefaults;
+ private final StartupStatusListener startupListener;
private static ProcessLauncherContext get() {
return DATA.get();
@@ -56,7 +57,7 @@ public class ProcessLauncherContext {
* @return true if this process should redirect output to the system log
*/
public static boolean isRedirectingOutput() {
- final ProcessLauncherContext context = get();
+ ProcessLauncherContext context = get();
if (context == null) {
return REDIRECT_OUTPUT_DEFAULT;
}
@@ -71,16 +72,15 @@ public class ProcessLauncherContext {
* @return the contingent gemfire properties values to be used as an alternative default value
*/
public static Properties getOverriddenDefaults() {
- final ProcessLauncherContext context = get();
+ ProcessLauncherContext context = get();
if (context == null) {
return OVERRIDDEN_DEFAULTS_DEFAULT;
}
return context.overriddenDefaults();
}
-
public static StartupStatusListener getStartupListener() {
- final ProcessLauncherContext context = get();
+ ProcessLauncherContext context = get();
if (context == null) {
return null;
}
@@ -91,9 +91,12 @@ public class ProcessLauncherContext {
/**
* Sets the ProcessLauncherContext data for the calling thread.
*/
- public static void set(final boolean redirectOutput, final Properties contingentProperties,
+ public static void set(final boolean redirectOutput, final Properties overriddenDefaults,
final StartupStatusListener startupListener) {
- DATA.set(new ProcessLauncherContext(redirectOutput, contingentProperties, startupListener));
+ notNull(overriddenDefaults,
+ "Invalid overriddenDefaults '" + overriddenDefaults + "' specified");
+
+ DATA.set(new ProcessLauncherContext(redirectOutput, overriddenDefaults, startupListener));
installLogListener(startupListener);
}
@@ -101,12 +104,11 @@ public class ProcessLauncherContext {
* Clears the current ProcessLauncherContext for the calling thread.
*/
public static void remove() {
- // DATA.get().restoreErrorStream();
DATA.remove();
clearLogListener();
}
- private static void installLogListener(StartupStatusListener startupListener) {
+ private static void installLogListener(final StartupStatusListener startupListener) {
if (startupListener != null) {
StartupStatus.setListener(startupListener);
}
@@ -116,11 +118,6 @@ public class ProcessLauncherContext {
StartupStatus.clearListener();
}
- private final boolean redirectOutput;
- private final Properties overriddenDefaults;
- private final StartupStatusListener startupListener;
- private PrintStream err;
-
private ProcessLauncherContext(final boolean redirectOutput, final Properties overriddenDefaults,
final StartupStatusListener startupListener) {
this.redirectOutput = redirectOutput;
@@ -129,39 +126,14 @@ public class ProcessLauncherContext {
}
private boolean redirectOutput() {
- return this.redirectOutput;
+ return redirectOutput;
}
private Properties overriddenDefaults() {
- return this.overriddenDefaults;
+ return overriddenDefaults;
}
private StartupStatusListener startupListener() {
- return this.startupListener;
- }
-
- @SuppressWarnings("unused")
- private void teeErrorStream() {
- final FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
- this.err = new PrintStream(new BufferedOutputStream(fdErr, 128), true);
- System.setErr(new TeePrintStream(new TeeOutputStream(new BufferedOutputStream(fdErr, 128))));
- }
-
- @SuppressWarnings("unused")
- private void restoreErrorStream() {
- if (System.err instanceof TeePrintStream) {
- final TeePrintStream tee = ((TeePrintStream) System.err);
- final OutputStream branch = tee.getTeeOutputStream().getBranchOutputStream();
-
- PrintStream newStdErr = null;
- if (branch == null) {
- newStdErr = this.err;
- } else if (branch instanceof PrintStream) {
- newStdErr = (PrintStream) branch;
- } else {
- newStdErr = new PrintStream(new BufferedOutputStream(branch, 128), true);
- }
- System.setErr(newStdErr);
- }
+ return startupListener;
}
}