From f54ae33236770d7cf180871e240bdb16bcefd3fd Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sat, 10 Jun 2017 15:16:50 +0300 Subject: [PATCH] =?UTF-8?q?Zond:=20=D0=BF=D0=B0=D1=82=D1=87=D0=B8=D0=BC=20?= =?UTF-8?q?Apache=20Exec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zond/build.gradle | 14 +- zond/src/main/java/asys/zond/Main.java | 4 +- .../org/apache/commons/exec/StreamPumper.java | 152 ++++++++++++++++++ 3 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 zond/src/main/java/org/apache/commons/exec/StreamPumper.java diff --git a/zond/build.gradle b/zond/build.gradle index 0f4e267..f01b79b 100644 --- a/zond/build.gradle +++ b/zond/build.gradle @@ -1,5 +1,5 @@ group = 'asys' -version = '0.3-SNAPSHOT' +version = '0.4-SNAPSHOT' apply plugin: 'application' @@ -7,16 +7,25 @@ mainClassName = "asys.zond.Main" configurations { included + includedEx compile.extendsFrom included + compile.extendsFrom includedEx } compileJava { dependsOn ':bridge-protocol:compileJava' } +def zp(FileTree ft) { + return ft.matching { + exclude 'org/apache/commons/exec/StreamPumper.class' + } +} + jar { dependsOn ':bridge-protocol:jar' dependsOn configurations.included + dependsOn configurations.includedEx manifest { attributes 'Implementation-Title': 'ASys Zond', 'Implementation-Version': version, @@ -24,6 +33,7 @@ jar { } baseName = project.group + '.' + project.name from { configurations.included.collect { it.isDirectory() ? it : zipTree(it) } } + from { configurations.includedEx.collect { it.isDirectory() ? it : zp(zipTree(it)) } } } ext { @@ -33,7 +43,7 @@ ext { dependencies { included files(project(':bridge-protocol').sourceSets.main.output.classesDir) included group: 'org.fusesource.jansi', name: 'jansi', version: '1.11' - included group: 'org.apache.commons', name: 'commons-exec', version: '1.3' + includedEx group: 'org.apache.commons', name: 'commons-exec', version: '1.3' included group: 'io.netty', name: 'netty-codec', version: nettyVersion included group: 'com.google.guava', name: 'guava', version: '21.0' } diff --git a/zond/src/main/java/asys/zond/Main.java b/zond/src/main/java/asys/zond/Main.java index 67ebdee..de47ea2 100644 --- a/zond/src/main/java/asys/zond/Main.java +++ b/zond/src/main/java/asys/zond/Main.java @@ -68,7 +68,9 @@ public class Main { PrintStream proxySysOut = new ProxySysOut(System.err); InputStream proxySysIn = new ProxySysIn(); - executor.setStreamHandler(new PumpStreamHandler(proxySysOut, proxySysOut, proxySysIn)); + PumpStreamHandler psh = new PumpStreamHandler(proxySysOut, proxySysOut, proxySysIn); + psh.setStopTimeout(-1999); //hack: по-умолчанию в Apache Exec добавляется еще 2000L милисекунд + executor.setStreamHandler(psh); int resultCode = 0; try { diff --git a/zond/src/main/java/org/apache/commons/exec/StreamPumper.java b/zond/src/main/java/org/apache/commons/exec/StreamPumper.java new file mode 100644 index 0000000..b50bc13 --- /dev/null +++ b/zond/src/main/java/org/apache/commons/exec/StreamPumper.java @@ -0,0 +1,152 @@ +/* + * 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.commons.exec; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.commons.exec.util.DebugUtils; + +/** + * Copies all data from an input stream to an output stream. + * + * @version $Id: StreamPumper.java 1557263 2014-01-10 21:18:09Z ggregory $ + */ +public class StreamPumper implements Runnable { + + /** the default size of the internal buffer for copying the streams */ + private static final int DEFAULT_SIZE = 1024; + + /** the input stream to pump from */ + private final InputStream is; + + /** the output stream to pmp into */ + private final OutputStream os; + + /** the size of the internal buffer for copying the streams */ + private final int size; + + /** was the end of the stream reached */ + private boolean finished; + + /** close the output stream when exhausted */ + private final boolean closeWhenExhausted; + + /** + * Create a new stream pumper. + * + * @param is input stream to read data from + * @param os output stream to write data to. + * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted. + */ + public StreamPumper(final InputStream is, final OutputStream os, + final boolean closeWhenExhausted) { + this.is = is; + this.os = os; + this.size = DEFAULT_SIZE; + this.closeWhenExhausted = closeWhenExhausted; + } + + /** + * Create a new stream pumper. + * + * @param is input stream to read data from + * @param os output stream to write data to. + * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted. + * @param size the size of the internal buffer for copying the streams + */ + public StreamPumper(final InputStream is, final OutputStream os, + final boolean closeWhenExhausted, final int size) { + this.is = is; + this.os = os; + this.size = size > 0 ? size : DEFAULT_SIZE; + this.closeWhenExhausted = closeWhenExhausted; + } + + /** + * Create a new stream pumper. + * + * @param is input stream to read data from + * @param os output stream to write data to. + */ + public StreamPumper(final InputStream is, final OutputStream os) { + this(is, os, false); + } + + /** + * Copies data from the input stream to the output stream. Terminates as + * soon as the input stream is closed or an error occurs. + */ + public void run() { + synchronized (this) { + // Just in case this object is reused in the future + finished = false; + } + + final byte[] buf = new byte[this.size]; + + int length; + try { + //hack: пропатчили алгоритм + while (!Thread.currentThread().isInterrupted() && (length = is.read(buf)) > 0) { + os.write(buf, 0, length); + os.flush(); + } + } catch (final Exception e) { + // nothing to do - happens quite often with watchdog + } finally { + if (closeWhenExhausted) { + try { + os.close(); + } catch (final IOException e) { + final String msg = "Got exception while closing exhausted output stream"; + DebugUtils.handleException(msg ,e); + } + } + synchronized (this) { + finished = true; + notifyAll(); + } + } + } + + /** + * Tells whether the end of the stream has been reached. + * + * @return true is the stream has been exhausted. + */ + public synchronized boolean isFinished() { + return finished; + } + + /** + * This method blocks until the stream pumper finishes. + * + * @exception InterruptedException + * if any thread interrupted the current thread before or while the current thread was waiting for a + * notification. + * @see #isFinished() + */ + public synchronized void waitFor() throws InterruptedException { + while (!isFinished()) { + wait(); + } + } +}