OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 177 | Rev 182 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils;
15
 
16
import java.io.BufferedReader;
17
import java.io.IOException;
18
import java.io.InputStream;
19
import java.io.InputStreamReader;
93 ilm 20
import java.io.OutputStream;
17 ilm 21
import java.io.PrintStream;
156 ilm 22
import java.lang.ProcessBuilder.Redirect;
17 ilm 23
import java.util.concurrent.Callable;
24
import java.util.concurrent.CountDownLatch;
25
import java.util.concurrent.ExecutorService;
26
import java.util.concurrent.Executors;
27
import java.util.concurrent.Future;
180 ilm 28
import java.util.function.Supplier;
17 ilm 29
 
30
/**
31
 * Redirect streams of a process to System.out and System.err.
180 ilm 32
 *
17 ilm 33
 * @author Sylvain
34
 */
35
public class ProcessStreams {
36
 
80 ilm 37
    static public enum Action {
38
        /**
39
         * Redirect process streams to ours.
180 ilm 40
         *
156 ilm 41
         * @deprecated use {@link ProcessStreams#redirect(ProcessBuilder)} (or
42
         *             {@link Redirect#INHERIT} directly) as it makes sure that
43
         *             {@link Process#waitFor()} only returns once all streams are flushed.
80 ilm 44
         */
45
        REDIRECT,
46
        /**
93 ilm 47
         * Consume streams.
80 ilm 48
         */
93 ilm 49
        CONSUME,
50
        /**
51
         * Close process streams. NOTE : some programs might fail (e.g. route on FreeBSD), in those
52
         * cases use {@link #CONSUME}.
53
         */
80 ilm 54
        CLOSE,
55
        /**
56
         * Do nothing, which is dangerous as the process will hang until its output is read.
57
         */
58
        DO_NOTHING
59
    }
60
 
177 ilm 61
    // Added to Java 9
62
    public static final Redirect DISCARD = Redirect.to(StreamUtils.NULL_FILE);
63
 
180 ilm 64
    static public final ProcessBuilder redirect(final ProcessBuilder pb) {
156 ilm 65
        return pb.redirectErrorStream(true).redirectOutput(Redirect.INHERIT);
66
    }
67
 
80 ilm 68
    static public final Process handle(final Process p, final Action action) throws IOException {
69
        if (action == Action.CLOSE) {
70
            p.getInputStream().close();
71
            p.getErrorStream().close();
72
        } else if (action == Action.REDIRECT) {
180 ilm 73
            new ProcessStreams(p, System.out, System.err);
93 ilm 74
        } else if (action == Action.CONSUME) {
75
            new ProcessStreams(p, StreamUtils.NULL_OS, StreamUtils.NULL_OS);
80 ilm 76
        }
77
        return p;
78
    }
79
 
17 ilm 80
    private final ExecutorService exec = Executors.newFixedThreadPool(2);
81
    private final CountDownLatch latch;
82
    private final Future<?> out;
83
    private final Future<?> err;
84
 
93 ilm 85
    /**
86
     * Create a new instance and start reading from the passed process. If a passed
87
     * {@link OutputStream} is <code>null</code>, then the corresponding {@link InputStream} is not
88
     * used at all, so the caller should handle it. If the output must be discarded, use
89
     * {@link StreamUtils#NULL_OS}.
180 ilm 90
     *
93 ilm 91
     * @param p the process to read from.
92
     * @param out where to write the {@link Process#getInputStream() standard output}.
93
     * @param err where to write the {@link Process#getErrorStream() standard error}.
94
     */
95
    public ProcessStreams(final Process p, final OutputStream out, final OutputStream err) {
17 ilm 96
        this.latch = new CountDownLatch(2);
180 ilm 97
        this.out = writeToAsync(p::getInputStream, out);
98
        this.err = writeToAsync(p::getErrorStream, err);
80 ilm 99
        this.exec.submit(new Runnable() {
180 ilm 100
            @Override
17 ilm 101
            public void run() {
102
                try {
103
                    ProcessStreams.this.latch.await();
180 ilm 104
                } catch (final InterruptedException e) {
17 ilm 105
                    // ne rien faire
106
                    e.printStackTrace();
107
                } finally {
108
                    ProcessStreams.this.exec.shutdown();
109
                }
110
            }
80 ilm 111
        });
17 ilm 112
    }
113
 
114
    protected final void stopOut() {
115
        this.stop(this.out);
116
    }
117
 
118
    protected final void stopErr() {
119
        this.stop(this.err);
120
    }
121
 
180 ilm 122
    private final void stop(final Future<?> f) {
93 ilm 123
        if (f == null)
124
            return;
17 ilm 125
        // TODO
126
        // ATTN don't interrupt, hangs in readLine()
127
        f.cancel(false);
128
    }
129
 
180 ilm 130
    private final Future<?> writeToAsync(final Supplier<InputStream> insSupplier, final Object outs) {
93 ilm 131
        if (outs == null) {
132
            this.latch.countDown();
133
            return null;
134
        }
17 ilm 135
        return this.exec.submit(new Callable<Object>() {
180 ilm 136
            @Override
137
            public Void call() throws InterruptedException, IOException {
138
                try (final InputStream ins = insSupplier.get()) {
93 ilm 139
                    // PrintStream is also an OutputStream
140
                    if (outs instanceof PrintStream)
141
                        writeTo(ins, (PrintStream) outs);
142
                    else
143
                        StreamUtils.copy(ins, (OutputStream) outs);
17 ilm 144
                    return null;
145
                } finally {
146
                    ProcessStreams.this.latch.countDown();
147
                }
148
            }
149
        });
150
    }
151
 
152
    /**
153
     * Copy ins to outs, line by line.
180 ilm 154
     *
17 ilm 155
     * @param ins the source.
156
     * @param outs the destination.
157
     * @throws InterruptedException if current thread is interrupted.
93 ilm 158
     * @throws IOException if I/O error.
17 ilm 159
     */
180 ilm 160
    public static final void writeTo(final InputStream ins, final PrintStream outs) throws InterruptedException, IOException {
17 ilm 161
        final BufferedReader r = new BufferedReader(new InputStreamReader(ins));
162
        String encodedName;
163
        while ((encodedName = r.readLine()) != null) {
164
            if (Thread.currentThread().isInterrupted()) {
165
                throw new InterruptedException();
166
            }
167
            outs.println(encodedName);
168
        }
169
    }
170
 
171
}