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    }