File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\maven-plugin\src\main\java\hudson\maven\SplittableBuildListener.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.maven;
25
26 import hudson.console.ConsoleNote;
27 import hudson.model.BuildListener;
28 import hudson.model.Cause;
29 import hudson.model.Result;
30 import hudson.model.StreamBuildListener;
31 import hudson.remoting.Callable;
32 import hudson.remoting.Channel;
33 import hudson.util.AbstractTaskListener;
34 import jenkins.util.MarkFindingOutputStream;
35 import org.apache.commons.io.output.ByteArrayOutputStream;
36 import org.apache.commons.io.output.DeferredFileOutputStream;
37
38 import java.io.File;
39 import java.io.IOException;
40 import java.io.OutputStream;
41 import java.io.PrintStream;
42 import java.io.PrintWriter;
43 import java.io.Serializable;
44 import java.io.UnsupportedEncodingException;
45 import java.util.List;
46
47 /**
48 * Delegating {@link BuildListener} that can have "side" {@link OutputStream}
49 * that gets log outputs. The side stream can be changed at runtime.
50 *
51 * @author Kohsuke Kawaguchi
52 * @since 1.133
53 */
54 final class SplittableBuildListener extends AbstractTaskListener implements BuildListener, Serializable {
55 /**
56 * The actual {@link BuildListener} where the output goes.
57 */
58 private final BuildListener core;
59
60 /**
61 * Used to accumulate data when no one is claiming the {@link #side},
62 * so that the next one who set the {@link #side} can claim all the data.
63 *
64 * {@link DeferredFileOutputStream} is used so that even if we get out of sync with Maven
65 * and end up accumulating a lot of data, we still won't kill the JVM.
66 */
67 private DeferredFileOutputStream unclaimed = newLog();
68
69 private volatile OutputStream side = unclaimed;
70
71 /**
72 * Constant {@link PrintStream} connected to both {@link #core} and {@link #side}.
73 * This is so that we can change the side stream without the client noticing it.
74 */
75 private final PrintStream logger;
76
77 private int markCount = 0;
78 private final Object markCountLock = new Object();
79
80 public SplittableBuildListener(BuildListener core) {
81 this.core = core;
82 final OutputStream base = core.getLogger();
83
84 final OutputStream tee = new OutputStream() {
85 public void write(int b) throws IOException {
86 base.write(b);
87 synchronized (lock()) {
88 side.write(b);
89 }
90 }
91
92 public void write(byte b[], int off, int len) throws IOException {
93 base.write(b, off, len);
94 synchronized (lock()) {
95 side.write(b, off, len);
96 }
97 }
98
99 public void flush() throws IOException {
100 base.flush();
101 synchronized (lock()) {
102 side.flush();
103 }
104 }
105
106 public void close() throws IOException {
107 base.close();
108 synchronized (lock()) {
109 side.close();
110 }
111 }
112 };
113
114 logger = new PrintStream(new MarkFindingOutputStream(tee) {
115 @Override
116 protected void onMarkFound() {
117 synchronized (markCountLock) {
118 markCount++;
119 markCountLock.notifyAll();
120 }
121 }
122 });
123 }
124
125 /**
126 * Mark/sync operation.
127 *
128 * <p>
129 * Where {@link SplittableBuildListener} is used, Jenkins is normally in control of the process
130 * that's generating the log (Maven, for example), and it's also the receiver.
131 *
132 * But because the stdout of the sender travels through a different route than the remoting channel,
133 * a synchronization needs to happen when we switch the side OutputStream.
134 *
135 * <p>
136 * This method does that synchronization by sending a marker string to the output, then
137 * block until we receive it. Provided that we are in control of the process generating the output,
138 * we will not receive any extra bytes after the marker string.
139 */
140 public void synchronizeOnMark(Channel ch) throws IOException, InterruptedException {
141 synchronized (markCountLock) {
142 int start = markCount;
143
144 // have the remote send us a mark
145 ch.call(new SendMark());
146
147 // and block until we receive a mark
148 while (markCount==start)
149 markCountLock.wait();
150 }
151 }
152
153 public void setSideOutputStream(OutputStream os) throws IOException {
154 synchronized (lock()) {
155 if(os==null) {
156 os = unclaimed;
157 } else {
158 unclaimed.close();
159 unclaimed.writeTo(os);
160 File f = unclaimed.getFile();
161 if (f!=null) f.delete();
162
163 unclaimed = newLog();
164 }
165 this.side = os;
166 }
167 }
168
169 private DeferredFileOutputStream newLog() {
170 return new DeferredFileOutputStream(10*1024,"maven-build","log",null);
171 }
172
173 /**
174 * We need to be able to atomically write the buffered bits and then create a fresh {@link ByteArrayOutputStream},
175 * when another thread (pipe I/O thread) is calling log.write().
176 *
177 * This locks controls the access and the write operation to {@link #side} (and since that can point to the same
178 * object as {@link #unclaimed}, that access needs to be in the same lock, too.)
179 */
180 private Object lock() {
181 return this;
182 }
183
184 public void started(List<Cause> causes) {
185 core.started(causes);
186 }
187
188 public void finished(Result result) {
189 core.finished(result);
190 }
191
192 public PrintStream getLogger() {
193 return logger;
194 }
195
196 public PrintWriter error(String msg) {
197 core.error(msg);
198 return new PrintWriter(logger);
199 }
200
201 public PrintWriter error(String format, Object... args) {
202 core.error(format,args);
203 return new PrintWriter(logger);
204 }
205
206 public PrintWriter fatalError(String msg) {
207 core.fatalError(msg);
208 return new PrintWriter(logger);
209 }
210
211 public PrintWriter fatalError(String format, Object... args) {
212 core.fatalError(format,args);
213 return new PrintWriter(logger);
214 }
215
216 public void annotate(ConsoleNote ann) throws IOException {
217 core.annotate(ann);
218 }
219
220 private Object writeReplace() throws IOException {
221 return new StreamBuildListener(logger);
222 }
223
224 private static final long serialVersionUID = 1L;
225
226 private static final byte[] MARK = toUTF8(MarkFindingOutputStream.MARK);
227
228 private static byte[] toUTF8(String s) {
229 try {
230 return s.getBytes("UTF-8");
231 } catch (UnsupportedEncodingException e) {
232 throw new AssertionError(e);
233 }
234 }
235
236 private static class SendMark implements Callable<Void, IOException> {
237 public Void call() throws IOException {
238 // write a mark
239 System.out.write(MARK);
240 System.out.flush();
241 return null;
242 }
243 }
244 }
245