org.apache.calcite.adapter.os.Processes Maven / Gradle / Ivy
Show all versions of calcite-plus Show documentation
/*
* 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.calcite.adapter.os;
import org.apache.calcite.linq4j.AbstractEnumerable;
import org.apache.calcite.linq4j.Enumerable;
import org.apache.calcite.linq4j.Enumerator;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.function.Supplier;
import static java.util.Objects.requireNonNull;
/**
* Utilities regarding operating system processes.
*
* WARNING: Spawning processes is not secure.
* Use this class with caution.
* This class is in the "plus" module because "plus" is not used by default.
* Do not move this class to the "core" module.
*/
public class Processes {
private Processes() {}
/** Executes a command and returns its result as an enumerable of lines. */
static Enumerable processLines(String... args) {
return processLines(' ', args);
}
/** Executes a command and returns its result as an enumerable of lines. */
static Enumerable processLines(char sep, String... args) {
return processLines(sep, processSupplier(args));
}
/** Executes a command and returns its result as an enumerable of lines.
*
* @param sep Separator character
* @param processSupplier Command and its arguments
*/
private static Enumerable processLines(char sep,
Supplier processSupplier) {
if (sep != ' ') {
return new SeparatedLinesEnumerable(processSupplier, sep);
} else {
return new ProcessLinesEnumerator(processSupplier);
}
}
private static Supplier processSupplier(final String... args) {
return new ProcessFactory(args);
}
/** Enumerator that executes a process and returns each line as an element. */
private static class ProcessLinesEnumerator
extends AbstractEnumerable {
private final Supplier processSupplier;
ProcessLinesEnumerator(Supplier processSupplier) {
this.processSupplier = requireNonNull(processSupplier, "processSupplier");
}
@Override public Enumerator enumerator() {
final Process process = processSupplier.get();
final InputStream is = process.getInputStream();
final BufferedInputStream bis =
new BufferedInputStream(is);
final InputStreamReader isr =
new InputStreamReader(bis, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(isr);
return new Enumerator() {
private String line;
@Override public String current() {
return line;
}
@Override public boolean moveNext() {
try {
line = br.readLine();
return line != null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override public void reset() {
throw new UnsupportedOperationException();
}
@Override public void close() {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException("while running " + processSupplier, e);
}
process.destroy();
}
};
}
}
/** Enumerator that executes a process and returns each line as an element. */
private static class SeparatedLinesEnumerable
extends AbstractEnumerable {
private final Supplier processSupplier;
private final int sep;
SeparatedLinesEnumerable(Supplier processSupplier, char sep) {
this.processSupplier = processSupplier;
this.sep = sep;
}
@Override public Enumerator enumerator() {
final Process process = processSupplier.get();
final InputStream is = process.getInputStream();
final BufferedInputStream bis =
new BufferedInputStream(is);
final InputStreamReader isr =
new InputStreamReader(bis, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(isr);
return new Enumerator() {
private final StringBuilder b = new StringBuilder();
private String line;
@Override public String current() {
return line;
}
@Override public boolean moveNext() {
try {
for (;;) {
int c = br.read();
if (c < 0) {
return false;
}
if (c == sep) {
line = b.toString();
b.setLength(0);
return true;
}
b.append((char) c);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override public void reset() {
throw new UnsupportedOperationException();
}
@Override public void close() {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException("while running " + processSupplier, e);
}
process.destroy();
}
};
}
}
/** Creates processes. */
private static class ProcessFactory implements Supplier {
private final String[] args;
ProcessFactory(String... args) {
this.args = args;
}
@Override public Process get() {
try {
return new ProcessBuilder().command(args).start();
} catch (IOException e) {
throw new RuntimeException("while creating process: "
+ Arrays.toString(args), e);
}
}
@Override public String toString() {
return args[0];
}
}
}