| /* |
| * 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 static java.util.concurrent.TimeUnit.MILLISECONDS; |
| import static org.apache.commons.lang3.StringUtils.isBlank; |
| import static org.apache.commons.lang3.Validate.isTrue; |
| import static org.apache.commons.lang3.Validate.notNull; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import org.apache.geode.internal.process.ControlFileWatchdog.ControlRequestHandler; |
| import org.apache.geode.lang.AttachAPINotFoundException; |
| |
| /** |
| * Controls a process using files. |
| * |
| * @since GemFire 8.0 |
| */ |
| class FileProcessController implements ProcessController { |
| |
| static final long DEFAULT_STATUS_TIMEOUT_MILLIS = 60 * 1000; |
| |
| private final long statusTimeoutMillis; |
| private final FileControllerParameters parameters; |
| private final int pid; |
| |
| /** |
| * Constructs an instance for controlling a local 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 |
| */ |
| FileProcessController(final FileControllerParameters parameters, final int pid) { |
| this(parameters, pid, DEFAULT_STATUS_TIMEOUT_MILLIS, MILLISECONDS); |
| } |
| |
| /** |
| * Constructs an instance for controlling a local 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 |
| */ |
| FileProcessController(final FileControllerParameters parameters, final int pid, |
| final long timeout, final TimeUnit units) { |
| 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.parameters = parameters; |
| this.statusTimeoutMillis = units.toMillis(timeout); |
| } |
| |
| @Override |
| public int getProcessId() { |
| return pid; |
| } |
| |
| @Override |
| public String status() |
| throws UnableToControlProcessException, IOException, InterruptedException, TimeoutException { |
| return status(parameters.getDirectory(), parameters.getProcessType().getStatusRequestFileName(), |
| parameters.getProcessType().getStatusFileName()); |
| } |
| |
| @Override |
| public void stop() throws UnableToControlProcessException, IOException { |
| stop(parameters.getDirectory(), parameters.getProcessType().getStopRequestFileName()); |
| } |
| |
| @Override |
| public void checkPidSupport() { |
| throw new AttachAPINotFoundException( |
| "The Attach API classes could not be found on the classpath. Please include JDK tools.jar on the classpath or add the JDK tools.jar to the jre/lib/ext directory."); |
| } |
| |
| long getStatusTimeoutMillis() { |
| return statusTimeoutMillis; |
| } |
| |
| private void stop(final File workingDir, final String stopRequestFileName) throws IOException { |
| File stopRequestFile = new File(workingDir, stopRequestFileName); |
| if (!stopRequestFile.exists()) { |
| stopRequestFile.createNewFile(); |
| } |
| } |
| |
| private String status(final File workingDir, final String statusRequestFileName, |
| final String statusFileName) throws IOException, InterruptedException, TimeoutException { |
| // monitor for statusFile |
| 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()); |
| } |
| }; |
| |
| ControlFileWatchdog statusFileWatchdog = |
| new ControlFileWatchdog(workingDir, statusFileName, statusHandler, true); |
| statusFileWatchdog.start(); |
| |
| File statusRequestFile = new File(workingDir, statusRequestFileName); |
| if (!statusRequestFile.exists()) { |
| statusRequestFile.createNewFile(); |
| } |
| |
| // if timeout invoke stop and then throw TimeoutException |
| long start = System.currentTimeMillis(); |
| while (statusFileWatchdog.isAlive()) { |
| Thread.sleep(10); |
| if (System.currentTimeMillis() >= start + statusTimeoutMillis) { |
| statusFileWatchdog.stop(); |
| throw new TimeoutException("Timed out waiting for process to create " + statusFile); |
| } |
| } |
| |
| String lines = statusRef.get(); |
| if (isBlank(lines)) { |
| throw new IllegalStateException("Status file '" + statusFile + "' is blank"); |
| } |
| return lines; |
| } |
| } |