
org.arquillian.droidium.container.impl.ProcessExecutor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of arquillian-droidium-container-impl Show documentation
Show all versions of arquillian-droidium-container-impl Show documentation
Arquillian Droidium Container Impl
The newest version!
/*
* JBoss, Home of Professional Open Source
* Copyright 2012, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.arquillian.droidium.container.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Executor service which is able to execute external process as well as callables
*
* @author Karel Piwko
*
*/
public class ProcessExecutor {
private final ShutDownThreadHolder shutdownThreads;
private final ExecutorService service;
private final ScheduledExecutorService scheduledService;
public ProcessExecutor() {
this.shutdownThreads = new ShutDownThreadHolder();
this.service = Executors.newCachedThreadPool();
this.scheduledService = Executors.newScheduledThreadPool(1);
}
/**
* Submit callable to be executed
*
* @param callable to be executed
* @return future
*/
public Future submit(Callable callable) {
return service.submit(callable);
}
/**
* Schedules a callable to be executed in regular intervals
*
* @param callable Callable
* @param timeout Total timeout
* @param step delay before next execution
* @param unit time unit
* @return {@code true} if executed successfully, false otherwise
* @throws InterruptedException
* @throws ExecutionException
*/
public Boolean scheduleUntilTrue(Callable callable, long timeout, long step, TimeUnit unit)
throws InterruptedException, ExecutionException {
CountDownWatch countdown = new CountDownWatch(timeout, unit);
while (countdown.timeLeft() > 0) {
// delay by step
ScheduledFuture future = scheduledService.schedule(callable, step, unit);
Boolean result = false;
try {
// wait for true up to timeLeft
// this means we might get less steps then timeout/step
result = future.get(countdown.timeLeft(), unit);
if (result == true) {
return true;
}
} catch (TimeoutException e) {
continue;
}
}
return false;
}
/**
* Spawns a process defined by command. Process output is discarded.
*
* @param command
* @return spawned process
* @throws InterruptedException
* @throws ExecutionException
*/
public Process spawn(List command) throws InterruptedException, ExecutionException {
return spawn(command.toArray(new String[0]));
}
/**
* Spawns a process defined by command. Process output is discarded
*
* @param command the command to be executed
* @return spawned process
* @throws InterruptedException
* @throws ExecutionException
*/
public Process spawn(String... command) throws InterruptedException, ExecutionException {
Future processFuture = service.submit(new SpawnedProcess(true, command));
Process process = processFuture.get();
service.submit(new ProcessOutputConsumer(new ProcessWithId(process, command[0])));
shutdownThreads.addHookFor(process);
return process;
}
/**
*
* @param input
* @param command
* @return pending results of the task
* @throws InterruptedException
* @throws ExecutionException
*/
public List execute(Map input, String... command) throws InterruptedException,
ExecutionException {
Future processFuture = service.submit(new SpawnedProcess(true, command));
Process process = processFuture.get();
return service.submit(new ProcessOutputConsumer(new ProcessWithId(process, command[0]), input)).get();
}
/**
*
* @param command
* @return pending results of the task
* @throws InterruptedException
* @throws ExecutionException
*/
public List execute(String... command) throws InterruptedException, ExecutionException {
return execute(Collections. emptyMap(), command);
}
public ProcessExecutor removeShutdownHook(Process p) {
shutdownThreads.removeHookFor(p);
return this;
}
private static class ShutDownThreadHolder {
private final Map shutdownThreads;
public ShutDownThreadHolder() {
this.shutdownThreads = Collections.synchronizedMap(new HashMap());
}
public void addHookFor(final Process p) {
Thread shutdownThread = new Thread(new Runnable() {
@Override
public void run() {
if (p != null) {
p.destroy();
try {
p.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
Runtime.getRuntime().addShutdownHook(shutdownThread);
shutdownThreads.put(p, shutdownThread);
}
public void removeHookFor(final Process p) {
shutdownThreads.remove(p);
}
}
private static class InputSanitizer {
public static List sanitizeArguments(String... command) {
List cmd = new ArrayList(command.length);
for (String c : command) {
if (c != null && c.length() > 0) {
cmd.add(c);
}
}
return cmd;
}
}
private static class SpawnedProcess implements Callable {
private final String[] command;
private boolean redirectErrorStream;
public SpawnedProcess(boolean redirectErrorStream, String... command) {
this.redirectErrorStream = redirectErrorStream;
this.command = command;
}
@Override
public Process call() throws Exception {
ProcessBuilder builder = new ProcessBuilder(InputSanitizer.sanitizeArguments(command));
builder.redirectErrorStream(redirectErrorStream);
return builder.start();
}
}
/**
* Runnable that consumes the output of the process.
*
* @author Stuart Douglas
* @author Karel Piwko
*/
private static class ProcessOutputConsumer implements Callable> {
private static final Logger log = Logger.getLogger(ProcessOutputConsumer.class.getName());
private static final String NL = System.getProperty("line.separator");
private final Process process;
private final Map inputOutputMap;
public ProcessOutputConsumer(ProcessWithId process, Map inputOutputMap) {
this.process = process;
this.inputOutputMap = inputOutputMap;
}
public ProcessOutputConsumer(ProcessWithId process) {
this(process, Collections. emptyMap());
}
@Override
public List call() throws Exception {
final InputStream stream = process.getInputStream();
final BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
final List output = new ArrayList();
try {
// read character by character
int i;
StringBuilder line = new StringBuilder();
while ((i = reader.read()) != -1) {
char c = (char) i;
// add the character
line.append(c);
// check if we are have to respond with an input
String key = line.toString();
if (inputOutputMap.containsKey(key)) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "{0} outputs: {1}, responded with: ",
new Object[] { process, line.toString(), inputOutputMap.get(key) });
}
OutputStream ostream = process.getOutputStream();
ostream.write(inputOutputMap.get(key).getBytes());
ostream.flush();
}
// save output
// adb command writes its output with ends of lines as "\\n"
// ignoring Windows conventions which recognize "\r\n" as the
// end of the line
if (line.indexOf("\n") != -1 || line.indexOf(NL) != -1) {
String wholeLine = line.toString();
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "{0} outputs: {1}", new Object[] { process, wholeLine });
} else if (wholeLine.toLowerCase().startsWith("error")) {
log.log(Level.SEVERE, "{0} outputs: {1}", new Object[] { process, wholeLine });
}
output.add(wholeLine);
line = new StringBuilder();
}
}
if (line.length() > 1) {
String wholeLine = line.toString();
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "{0} outputs: {1}", new Object[] { process, wholeLine });
} else if (wholeLine.toLowerCase().startsWith("error")) {
log.log(Level.SEVERE, "{0} outputs: {1}", new Object[] { process, wholeLine });
}
output.add(wholeLine);
}
} catch (IOException e) {
}
return output;
}
}
/**
* Represents a proccess with id
*
* @author Karel Piwko
*
*/
private class ProcessWithId extends Process {
private final Process process;
private final String id;
public ProcessWithId(Process process, String id) {
this.id = id;
this.process = process;
}
@Override
public OutputStream getOutputStream() {
return process.getOutputStream();
}
@Override
public InputStream getInputStream() {
return process.getInputStream();
}
@Override
public InputStream getErrorStream() {
return process.getErrorStream();
}
@Override
public int waitFor() throws InterruptedException {
return process.waitFor();
}
@Override
public int exitValue() {
return process.exitValue();
}
@Override
public void destroy() {
process.destroy();
}
@Override
public String toString() {
return "Process: " + id;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy