001package 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
022import java.io.ByteArrayOutputStream;
023import java.io.IOException;
024import java.io.PrintStream;
025import java.util.Collections;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.Iterator;
029import java.util.Map;
030import 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" } )
041public 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}