OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 93 | Rev 177 | 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
 
156 ilm 60
    static public final ProcessBuilder redirect(final ProcessBuilder pb) throws IOException {
61
        return pb.redirectErrorStream(true).redirectOutput(Redirect.INHERIT);
62
    }
63
 
80 ilm 64
    static public final Process handle(final Process p, final Action action) throws IOException {
65
        if (action == Action.CLOSE) {
66
            p.getInputStream().close();
67
            p.getErrorStream().close();
68
        } else if (action == Action.REDIRECT) {
69
            new ProcessStreams(p);
93 ilm 70
        } else if (action == Action.CONSUME) {
71
            new ProcessStreams(p, StreamUtils.NULL_OS, StreamUtils.NULL_OS);
80 ilm 72
        }
73
        return p;
74
    }
75
 
17 ilm 76
    private final ExecutorService exec = Executors.newFixedThreadPool(2);
77
    private final CountDownLatch latch;
78
    private final Future<?> out;
79
    private final Future<?> err;
80
 
81
    public ProcessStreams(final Process p) {
93 ilm 82
        this(p, System.out, System.err);
83
    }
84
 
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}.
90
     *
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);
93 ilm 97
        this.out = writeToAsync(p.getInputStream(), out);
98
        this.err = writeToAsync(p.getErrorStream(), err);
80 ilm 99
        this.exec.submit(new Runnable() {
17 ilm 100
            public void run() {
101
                try {
102
                    ProcessStreams.this.latch.await();
103
                } catch (InterruptedException e) {
104
                    // ne rien faire
105
                    e.printStackTrace();
106
                } finally {
107
                    ProcessStreams.this.exec.shutdown();
108
                }
109
            }
80 ilm 110
        });
17 ilm 111
    }
112
 
113
    protected final void stopOut() {
114
        this.stop(this.out);
115
    }
116
 
117
    protected final void stopErr() {
118
        this.stop(this.err);
119
    }
120
 
121
    private final void stop(Future<?> f) {
93 ilm 122
        if (f == null)
123
            return;
17 ilm 124
        // TODO
125
        // ATTN don't interrupt, hangs in readLine()
126
        f.cancel(false);
127
    }
128
 
93 ilm 129
    private final Future<?> writeToAsync(final InputStream ins, final Object outs) {
130
        if (outs == null) {
131
            this.latch.countDown();
132
            return null;
133
        }
17 ilm 134
        return this.exec.submit(new Callable<Object>() {
135
            public Object call() throws InterruptedException, IOException {
136
                try {
93 ilm 137
                    // PrintStream is also an OutputStream
138
                    if (outs instanceof PrintStream)
139
                        writeTo(ins, (PrintStream) outs);
140
                    else
141
                        StreamUtils.copy(ins, (OutputStream) outs);
142
                    ins.close();
17 ilm 143
                    return null;
144
                } finally {
145
                    ProcessStreams.this.latch.countDown();
146
                }
147
            }
148
        });
149
    }
150
 
151
    /**
152
     * Copy ins to outs, line by line.
153
     *
154
     * @param ins the source.
155
     * @param outs the destination.
156
     * @throws InterruptedException if current thread is interrupted.
93 ilm 157
     * @throws IOException if I/O error.
17 ilm 158
     */
159
    public static final void writeTo(InputStream ins, PrintStream outs) throws InterruptedException, IOException {
160
        final BufferedReader r = new BufferedReader(new InputStreamReader(ins));
161
        String encodedName;
162
        while ((encodedName = r.readLine()) != null) {
163
            if (Thread.currentThread().isInterrupted()) {
164
                throw new InterruptedException();
165
            }
166
            outs.println(encodedName);
167
        }
168
    }
169
 
170
}