OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 156 | Rev 180 | 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;
28
 
29
/**
30
 * Redirect streams of a process to System.out and System.err.
31
 *
32
 * @author Sylvain
33
 */
34
public class ProcessStreams {
35
 
80 ilm 36
    static public enum Action {
37
        /**
38
         * Redirect process streams to ours.
156 ilm 39
         *
40
         * @deprecated use {@link ProcessStreams#redirect(ProcessBuilder)} (or
41
         *             {@link Redirect#INHERIT} directly) as it makes sure that
42
         *             {@link Process#waitFor()} only returns once all streams are flushed.
80 ilm 43
         */
44
        REDIRECT,
45
        /**
93 ilm 46
         * Consume streams.
80 ilm 47
         */
93 ilm 48
        CONSUME,
49
        /**
50
         * Close process streams. NOTE : some programs might fail (e.g. route on FreeBSD), in those
51
         * cases use {@link #CONSUME}.
52
         */
80 ilm 53
        CLOSE,
54
        /**
55
         * Do nothing, which is dangerous as the process will hang until its output is read.
56
         */
57
        DO_NOTHING
58
    }
59
 
177 ilm 60
    // Added to Java 9
61
    public static final Redirect DISCARD = Redirect.to(StreamUtils.NULL_FILE);
62
 
156 ilm 63
    static public final ProcessBuilder redirect(final ProcessBuilder pb) throws IOException {
64
        return pb.redirectErrorStream(true).redirectOutput(Redirect.INHERIT);
65
    }
66
 
80 ilm 67
    static public final Process handle(final Process p, final Action action) throws IOException {
68
        if (action == Action.CLOSE) {
69
            p.getInputStream().close();
70
            p.getErrorStream().close();
71
        } else if (action == Action.REDIRECT) {
72
            new ProcessStreams(p);
93 ilm 73
        } else if (action == Action.CONSUME) {
74
            new ProcessStreams(p, StreamUtils.NULL_OS, StreamUtils.NULL_OS);
80 ilm 75
        }
76
        return p;
77
    }
78
 
17 ilm 79
    private final ExecutorService exec = Executors.newFixedThreadPool(2);
80
    private final CountDownLatch latch;
81
    private final Future<?> out;
82
    private final Future<?> err;
83
 
84
    public ProcessStreams(final Process p) {
93 ilm 85
        this(p, System.out, System.err);
86
    }
87
 
88
    /**
89
     * Create a new instance and start reading from the passed process. If a passed
90
     * {@link OutputStream} is <code>null</code>, then the corresponding {@link InputStream} is not
91
     * used at all, so the caller should handle it. If the output must be discarded, use
92
     * {@link StreamUtils#NULL_OS}.
93
     *
94
     * @param p the process to read from.
95
     * @param out where to write the {@link Process#getInputStream() standard output}.
96
     * @param err where to write the {@link Process#getErrorStream() standard error}.
97
     */
98
    public ProcessStreams(final Process p, final OutputStream out, final OutputStream err) {
17 ilm 99
        this.latch = new CountDownLatch(2);
93 ilm 100
        this.out = writeToAsync(p.getInputStream(), out);
101
        this.err = writeToAsync(p.getErrorStream(), err);
80 ilm 102
        this.exec.submit(new Runnable() {
17 ilm 103
            public void run() {
104
                try {
105
                    ProcessStreams.this.latch.await();
106
                } catch (InterruptedException e) {
107
                    // ne rien faire
108
                    e.printStackTrace();
109
                } finally {
110
                    ProcessStreams.this.exec.shutdown();
111
                }
112
            }
80 ilm 113
        });
17 ilm 114
    }
115
 
116
    protected final void stopOut() {
117
        this.stop(this.out);
118
    }
119
 
120
    protected final void stopErr() {
121
        this.stop(this.err);
122
    }
123
 
124
    private final void stop(Future<?> f) {
93 ilm 125
        if (f == null)
126
            return;
17 ilm 127
        // TODO
128
        // ATTN don't interrupt, hangs in readLine()
129
        f.cancel(false);
130
    }
131
 
93 ilm 132
    private final Future<?> writeToAsync(final InputStream ins, final Object outs) {
133
        if (outs == null) {
134
            this.latch.countDown();
135
            return null;
136
        }
17 ilm 137
        return this.exec.submit(new Callable<Object>() {
138
            public Object call() throws InterruptedException, IOException {
139
                try {
93 ilm 140
                    // PrintStream is also an OutputStream
141
                    if (outs instanceof PrintStream)
142
                        writeTo(ins, (PrintStream) outs);
143
                    else
144
                        StreamUtils.copy(ins, (OutputStream) outs);
145
                    ins.close();
17 ilm 146
                    return null;
147
                } finally {
148
                    ProcessStreams.this.latch.countDown();
149
                }
150
            }
151
        });
152
    }
153
 
154
    /**
155
     * Copy ins to outs, line by line.
156
     *
157
     * @param ins the source.
158
     * @param outs the destination.
159
     * @throws InterruptedException if current thread is interrupted.
93 ilm 160
     * @throws IOException if I/O error.
17 ilm 161
     */
162
    public static final void writeTo(InputStream ins, PrintStream outs) throws InterruptedException, IOException {
163
        final BufferedReader r = new BufferedReader(new InputStreamReader(ins));
164
        String encodedName;
165
        while ((encodedName = r.readLine()) != null) {
166
            if (Thread.currentThread().isInterrupted()) {
167
                throw new InterruptedException();
168
            }
169
            outs.println(encodedName);
170
        }
171
    }
172
 
173
}