View Javadoc

1   package org.apache.maven.lifecycle.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.io.PrintStream;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.Map;
30  import java.util.Set;
31  
32  /**
33   * @since 3.0
34   * @author Kristian Rosenvold
35   *         <p/>
36   *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
37   *         This class in particular may spontaneusly self-combust and be replaced by a plexus-compliant thread aware
38   *         logger implementation at any time.
39   */
40  @SuppressWarnings( { "SynchronizationOnLocalVariableOrMethodParameter" } )
41  public class ThreadOutputMuxer
42  {
43      private final Iterator<ProjectSegment> projects;
44  
45      private final ThreadLocal<ProjectSegment> projectBuildThreadLocal = new ThreadLocal<ProjectSegment>();
46  
47      private final Map<ProjectSegment, ByteArrayOutputStream> streams =
48          new HashMap<ProjectSegment, ByteArrayOutputStream>();
49  
50      private final Map<ProjectSegment, PrintStream> printStreams = new HashMap<ProjectSegment, PrintStream>();
51  
52      private final ByteArrayOutputStream defaultOutputStreamForUnknownData = new ByteArrayOutputStream();
53  
54      private final PrintStream defaultPringStream = new PrintStream( defaultOutputStreamForUnknownData );
55  
56      private final Set<ProjectSegment> completedBuilds = Collections.synchronizedSet( new HashSet<ProjectSegment>() );
57  
58      private volatile ProjectSegment currentBuild;
59  
60      private final PrintStream originalSystemOUtStream;
61  
62      private final ConsolePrinter printer;
63  
64      /**
65       * A simple but safe solution for printing to the console.
66       */
67  
68      class ConsolePrinter
69          implements Runnable
70      {
71          public volatile boolean running;
72  
73          private final ProjectBuildList projectBuildList;
74  
75          ConsolePrinter( ProjectBuildList projectBuildList )
76          {
77              this.projectBuildList = projectBuildList;
78          }
79  
80          public void run()
81          {
82              running = true;
83              for ( ProjectSegment projectBuild : projectBuildList )
84              {
85                  final PrintStream projectStream = printStreams.get( projectBuild );
86                  ByteArrayOutputStream projectOs = streams.get( projectBuild );
87  
88                  do
89                  {
90                      synchronized ( projectStream )
91                      {
92                          try
93                          {
94                              projectStream.wait( 100 );
95                          }
96                          catch ( InterruptedException e )
97                          {
98                              throw new RuntimeException( e );
99                          }
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 }