package org.apache.maven.lifecycle.internal; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * @since 3.0 * @author Kristian Rosenvold * <p/> * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. * This class in particular may spontaneusly self-combust and be replaced by a plexus-compliant thread aware * logger implementation at any time. */ @SuppressWarnings( { "SynchronizationOnLocalVariableOrMethodParameter" } ) public class ThreadOutputMuxer { private final Iterator<ProjectSegment> projects; private final ThreadLocal<ProjectSegment> projectBuildThreadLocal = new ThreadLocal<ProjectSegment>(); private final Map<ProjectSegment, ByteArrayOutputStream> streams = new HashMap<ProjectSegment, ByteArrayOutputStream>(); private final Map<ProjectSegment, PrintStream> printStreams = new HashMap<ProjectSegment, PrintStream>(); private final ByteArrayOutputStream defaultOutputStreamForUnknownData = new ByteArrayOutputStream(); private final PrintStream defaultPringStream = new PrintStream( defaultOutputStreamForUnknownData ); private final Set<ProjectSegment> completedBuilds = Collections.synchronizedSet( new HashSet<ProjectSegment>() ); private volatile ProjectSegment currentBuild; private final PrintStream originalSystemOUtStream; private final ConsolePrinter printer; /** * A simple but safe solution for printing to the console. */ class ConsolePrinter implements Runnable { public volatile boolean running; private final ProjectBuildList projectBuildList; ConsolePrinter( ProjectBuildList projectBuildList ) { this.projectBuildList = projectBuildList; } public void run() { running = true; for ( ProjectSegment projectBuild : projectBuildList ) { final PrintStream projectStream = printStreams.get( projectBuild ); ByteArrayOutputStream projectOs = streams.get( projectBuild ); do { synchronized ( projectStream ) { try { projectStream.wait( 100 ); } catch ( InterruptedException e ) { throw new RuntimeException( e ); } try { projectOs.writeTo( originalSystemOUtStream ); } catch ( IOException e ) { throw new RuntimeException( e ); } projectOs.reset(); } } while ( !completedBuilds.contains( projectBuild ) ); } running = false; } /* Wait until we are sure the print-stream thread is running. */ public void waitUntilRunning( boolean expect ) { while ( !running == expect ) { try { Thread.sleep( 10 ); } catch ( InterruptedException e ) { throw new RuntimeException( e ); } } } } public ThreadOutputMuxer( ProjectBuildList segmentChunks, PrintStream originalSystemOut ) { projects = segmentChunks.iterator(); for ( ProjectSegment segmentChunk : segmentChunks ) { final ByteArrayOutputStream value = new ByteArrayOutputStream(); streams.put( segmentChunk, value ); printStreams.put( segmentChunk, new PrintStream( value ) ); } setNext(); this.originalSystemOUtStream = originalSystemOut; System.setOut( new ThreadBoundPrintStream( this.originalSystemOUtStream ) ); printer = new ConsolePrinter( segmentChunks ); new Thread( printer ).start(); printer.waitUntilRunning( true ); } public void close() { printer.waitUntilRunning( false ); System.setOut( this.originalSystemOUtStream ); } private void setNext() { currentBuild = projects.hasNext() ? projects.next() : null; } private boolean ownsRealOutputStream( ProjectSegment projectBuild ) { return projectBuild.equals( currentBuild ); } private PrintStream getThreadBoundPrintStream() { ProjectSegment threadProject = projectBuildThreadLocal.get(); if ( threadProject == null ) { return defaultPringStream; } if ( ownsRealOutputStream( threadProject ) ) { return originalSystemOUtStream; } return printStreams.get( threadProject ); } public void associateThreadWithProjectSegment( ProjectSegment projectBuild ) { projectBuildThreadLocal.set( projectBuild ); } public void setThisModuleComplete( ProjectSegment projectBuild ) { completedBuilds.add( projectBuild ); PrintStream stream = printStreams.get( projectBuild ); synchronized ( stream ) { stream.notifyAll(); } disconnectThreadFromProject(); } private void disconnectThreadFromProject() { projectBuildThreadLocal.remove(); } private class ThreadBoundPrintStream extends PrintStream { public ThreadBoundPrintStream( PrintStream systemOutStream ) { super( systemOutStream ); } private PrintStream getOutputStreamForCurrentThread() { return getThreadBoundPrintStream(); } @Override public void println() { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.println(); currentStream.notifyAll(); } } @Override public void print( char c ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( c ); currentStream.notifyAll(); } } @Override public void println( char x ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.println( x ); currentStream.notifyAll(); } } @Override public void print( double d ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( d ); currentStream.notifyAll(); } } @Override public void println( double x ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.println( x ); currentStream.notifyAll(); } } @Override public void print( float f ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( f ); currentStream.notifyAll(); } } @Override public void println( float x ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.println( x ); currentStream.notifyAll(); } } @Override public void print( int i ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( i ); currentStream.notifyAll(); } } @Override public void println( int x ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.println( x ); currentStream.notifyAll(); } } @Override public void print( long l ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( l ); currentStream.notifyAll(); } } @Override public void println( long x ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( x ); currentStream.notifyAll(); } } @Override public void print( boolean b ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( b ); currentStream.notifyAll(); } } @Override public void println( boolean x ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( x ); currentStream.notifyAll(); } } @Override public void print( char s[] ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( s ); currentStream.notifyAll(); } } @Override public void println( char x[] ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( x ); currentStream.notifyAll(); } } @Override public void print( Object obj ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( obj ); currentStream.notifyAll(); } } @Override public void println( Object x ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.println( x ); currentStream.notifyAll(); } } @Override public void print( String s ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.print( s ); currentStream.notifyAll(); } } @Override public void println( String x ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.println( x ); currentStream.notifyAll(); } } @Override public void write( byte b[], int off, int len ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.write( b, off, len ); currentStream.notifyAll(); } } @Override public void close() { getOutputStreamForCurrentThread().close(); } @Override public void flush() { getOutputStreamForCurrentThread().flush(); } @Override public void write( int b ) { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.write( b ); currentStream.notifyAll(); } } @Override public void write( byte b[] ) throws IOException { final PrintStream currentStream = getOutputStreamForCurrentThread(); synchronized ( currentStream ) { currentStream.write( b ); currentStream.notifyAll(); } } } }