001 package org.apache.maven.lifecycle.internal;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.io.ByteArrayOutputStream;
023 import java.io.IOException;
024 import java.io.PrintStream;
025 import java.util.Collections;
026 import java.util.HashMap;
027 import java.util.HashSet;
028 import java.util.Iterator;
029 import java.util.Map;
030 import java.util.Set;
031
032 /**
033 * @since 3.0
034 * @author Kristian Rosenvold
035 * <p/>
036 * NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
037 * This class in particular may spontaneusly self-combust and be replaced by a plexus-compliant thread aware
038 * logger implementation at any time.
039 */
040 @SuppressWarnings( { "SynchronizationOnLocalVariableOrMethodParameter" } )
041 public class ThreadOutputMuxer
042 {
043 private final Iterator<ProjectSegment> projects;
044
045 private final ThreadLocal<ProjectSegment> projectBuildThreadLocal = new ThreadLocal<ProjectSegment>();
046
047 private final Map<ProjectSegment, ByteArrayOutputStream> streams =
048 new HashMap<ProjectSegment, ByteArrayOutputStream>();
049
050 private final Map<ProjectSegment, PrintStream> printStreams = new HashMap<ProjectSegment, PrintStream>();
051
052 private final ByteArrayOutputStream defaultOutputStreamForUnknownData = new ByteArrayOutputStream();
053
054 private final PrintStream defaultPringStream = new PrintStream( defaultOutputStreamForUnknownData );
055
056 private final Set<ProjectSegment> completedBuilds = Collections.synchronizedSet( new HashSet<ProjectSegment>() );
057
058 private volatile ProjectSegment currentBuild;
059
060 private final PrintStream originalSystemOUtStream;
061
062 private final ConsolePrinter printer;
063
064 /**
065 * A simple but safe solution for printing to the console.
066 */
067
068 class ConsolePrinter
069 implements Runnable
070 {
071 public volatile boolean running;
072
073 private final ProjectBuildList projectBuildList;
074
075 ConsolePrinter( ProjectBuildList projectBuildList )
076 {
077 this.projectBuildList = projectBuildList;
078 }
079
080 public void run()
081 {
082 running = true;
083 for ( ProjectSegment projectBuild : projectBuildList )
084 {
085 final PrintStream projectStream = printStreams.get( projectBuild );
086 ByteArrayOutputStream projectOs = streams.get( projectBuild );
087
088 do
089 {
090 synchronized ( projectStream )
091 {
092 try
093 {
094 projectStream.wait( 100 );
095 }
096 catch ( InterruptedException e )
097 {
098 throw new RuntimeException( e );
099 }
100 try
101 {
102 projectOs.writeTo( originalSystemOUtStream );
103 }
104 catch ( IOException e )
105 {
106 throw new RuntimeException( e );
107 }
108
109 projectOs.reset();
110 }
111 }
112 while ( !completedBuilds.contains( projectBuild ) );
113 }
114 running = false;
115 }
116
117 /*
118 Wait until we are sure the print-stream thread is running.
119 */
120
121 public void waitUntilRunning( boolean expect )
122 {
123 while ( !running == expect )
124 {
125 try
126 {
127 Thread.sleep( 10 );
128 }
129 catch ( InterruptedException e )
130 {
131 throw new RuntimeException( e );
132 }
133 }
134 }
135 }
136
137 public ThreadOutputMuxer( ProjectBuildList segmentChunks, PrintStream originalSystemOut )
138 {
139 projects = segmentChunks.iterator();
140 for ( ProjectSegment segmentChunk : segmentChunks )
141 {
142 final ByteArrayOutputStream value = new ByteArrayOutputStream();
143 streams.put( segmentChunk, value );
144 printStreams.put( segmentChunk, new PrintStream( value ) );
145 }
146 setNext();
147 this.originalSystemOUtStream = originalSystemOut;
148 System.setOut( new ThreadBoundPrintStream( this.originalSystemOUtStream ) );
149 printer = new ConsolePrinter( segmentChunks );
150 new Thread( printer ).start();
151 printer.waitUntilRunning( true );
152 }
153
154 public void close()
155 {
156 printer.waitUntilRunning( false );
157 System.setOut( this.originalSystemOUtStream );
158 }
159
160 private void setNext()
161 {
162 currentBuild = projects.hasNext() ? projects.next() : null;
163 }
164
165 private boolean ownsRealOutputStream( ProjectSegment projectBuild )
166 {
167 return projectBuild.equals( currentBuild );
168 }
169
170 private PrintStream getThreadBoundPrintStream()
171 {
172 ProjectSegment threadProject = projectBuildThreadLocal.get();
173 if ( threadProject == null )
174 {
175 return defaultPringStream;
176 }
177 if ( ownsRealOutputStream( threadProject ) )
178 {
179 return originalSystemOUtStream;
180 }
181 return printStreams.get( threadProject );
182 }
183
184 public void associateThreadWithProjectSegment( ProjectSegment projectBuild )
185 {
186 projectBuildThreadLocal.set( projectBuild );
187 }
188
189 public void setThisModuleComplete( ProjectSegment projectBuild )
190 {
191 completedBuilds.add( projectBuild );
192 PrintStream stream = printStreams.get( projectBuild );
193 synchronized ( stream )
194 {
195 stream.notifyAll();
196 }
197 disconnectThreadFromProject();
198 }
199
200 private void disconnectThreadFromProject()
201 {
202 projectBuildThreadLocal.remove();
203 }
204
205 private class ThreadBoundPrintStream
206 extends PrintStream
207 {
208
209 public ThreadBoundPrintStream( PrintStream systemOutStream )
210 {
211 super( systemOutStream );
212 }
213
214 private PrintStream getOutputStreamForCurrentThread()
215 {
216 return getThreadBoundPrintStream();
217 }
218
219 @Override
220 public void println()
221 {
222 final PrintStream currentStream = getOutputStreamForCurrentThread();
223 synchronized ( currentStream )
224 {
225 currentStream.println();
226 currentStream.notifyAll();
227 }
228 }
229
230 @Override
231 public void print( char c )
232 {
233 final PrintStream currentStream = getOutputStreamForCurrentThread();
234 synchronized ( currentStream )
235 {
236 currentStream.print( c );
237 currentStream.notifyAll();
238 }
239 }
240
241 @Override
242 public void println( char x )
243 {
244 final PrintStream currentStream = getOutputStreamForCurrentThread();
245 synchronized ( currentStream )
246 {
247 currentStream.println( x );
248 currentStream.notifyAll();
249 }
250 }
251
252 @Override
253 public void print( double d )
254 {
255 final PrintStream currentStream = getOutputStreamForCurrentThread();
256 synchronized ( currentStream )
257 {
258 currentStream.print( d );
259 currentStream.notifyAll();
260 }
261 }
262
263 @Override
264 public void println( double x )
265 {
266 final PrintStream currentStream = getOutputStreamForCurrentThread();
267 synchronized ( currentStream )
268 {
269 currentStream.println( x );
270 currentStream.notifyAll();
271 }
272 }
273
274 @Override
275 public void print( float f )
276 {
277 final PrintStream currentStream = getOutputStreamForCurrentThread();
278 synchronized ( currentStream )
279 {
280 currentStream.print( f );
281 currentStream.notifyAll();
282 }
283 }
284
285 @Override
286 public void println( float x )
287 {
288 final PrintStream currentStream = getOutputStreamForCurrentThread();
289 synchronized ( currentStream )
290 {
291 currentStream.println( x );
292 currentStream.notifyAll();
293 }
294 }
295
296 @Override
297 public void print( int i )
298 {
299 final PrintStream currentStream = getOutputStreamForCurrentThread();
300 synchronized ( currentStream )
301 {
302 currentStream.print( i );
303 currentStream.notifyAll();
304 }
305 }
306
307 @Override
308 public void println( int x )
309 {
310 final PrintStream currentStream = getOutputStreamForCurrentThread();
311 synchronized ( currentStream )
312 {
313 currentStream.println( x );
314 currentStream.notifyAll();
315 }
316 }
317
318 @Override
319 public void print( long l )
320 {
321 final PrintStream currentStream = getOutputStreamForCurrentThread();
322 synchronized ( currentStream )
323 {
324 currentStream.print( l );
325 currentStream.notifyAll();
326 }
327 }
328
329 @Override
330 public void println( long x )
331 {
332 final PrintStream currentStream = getOutputStreamForCurrentThread();
333 synchronized ( currentStream )
334 {
335 currentStream.print( x );
336 currentStream.notifyAll();
337 }
338 }
339
340 @Override
341 public void print( boolean b )
342 {
343 final PrintStream currentStream = getOutputStreamForCurrentThread();
344 synchronized ( currentStream )
345 {
346 currentStream.print( b );
347 currentStream.notifyAll();
348 }
349 }
350
351 @Override
352 public void println( boolean x )
353 {
354 final PrintStream currentStream = getOutputStreamForCurrentThread();
355 synchronized ( currentStream )
356 {
357 currentStream.print( x );
358 currentStream.notifyAll();
359 }
360 }
361
362 @Override
363 public void print( char s[] )
364 {
365 final PrintStream currentStream = getOutputStreamForCurrentThread();
366 synchronized ( currentStream )
367 {
368 currentStream.print( s );
369 currentStream.notifyAll();
370 }
371 }
372
373 @Override
374 public void println( char x[] )
375 {
376 final PrintStream currentStream = getOutputStreamForCurrentThread();
377 synchronized ( currentStream )
378 {
379 currentStream.print( x );
380 currentStream.notifyAll();
381 }
382 }
383
384 @Override
385 public void print( Object obj )
386 {
387 final PrintStream currentStream = getOutputStreamForCurrentThread();
388 synchronized ( currentStream )
389 {
390 currentStream.print( obj );
391 currentStream.notifyAll();
392 }
393 }
394
395 @Override
396 public void println( Object x )
397 {
398 final PrintStream currentStream = getOutputStreamForCurrentThread();
399 synchronized ( currentStream )
400 {
401 currentStream.println( x );
402 currentStream.notifyAll();
403 }
404 }
405
406 @Override
407 public void print( String s )
408 {
409 final PrintStream currentStream = getOutputStreamForCurrentThread();
410 synchronized ( currentStream )
411 {
412 currentStream.print( s );
413 currentStream.notifyAll();
414 }
415 }
416
417 @Override
418 public void println( String x )
419 {
420 final PrintStream currentStream = getOutputStreamForCurrentThread();
421 synchronized ( currentStream )
422 {
423 currentStream.println( x );
424 currentStream.notifyAll();
425 }
426 }
427
428 @Override
429 public void write( byte b[], int off, int len )
430 {
431 final PrintStream currentStream = getOutputStreamForCurrentThread();
432 synchronized ( currentStream )
433 {
434 currentStream.write( b, off, len );
435 currentStream.notifyAll();
436 }
437 }
438
439 @Override
440 public void close()
441 {
442 getOutputStreamForCurrentThread().close();
443 }
444
445 @Override
446 public void flush()
447 {
448 getOutputStreamForCurrentThread().flush();
449 }
450
451 @Override
452 public void write( int b )
453 {
454 final PrintStream currentStream = getOutputStreamForCurrentThread();
455 synchronized ( currentStream )
456 {
457 currentStream.write( b );
458 currentStream.notifyAll();
459 }
460 }
461
462 @Override
463 public void write( byte b[] )
464 throws IOException
465 {
466 final PrintStream currentStream = getOutputStreamForCurrentThread();
467 synchronized ( currentStream )
468 {
469 currentStream.write( b );
470 currentStream.notifyAll();
471 }
472 }
473 }
474 }