blob: 273570b68e9ebcada4fa6526a7b826a1cd16101e [file] [log] [blame]
package org.apache.maven.lifecycle.internal.builder.multithreaded;
/*
* 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;
import org.apache.maven.lifecycle.internal.ProjectBuildList;
import org.apache.maven.lifecycle.internal.ProjectSegment;
/**
* <strong>NOTE:</strong> This class is not part of any public api and can be changed or deleted without prior notice.
* This class in particular may spontaneously self-combust and be replaced by a plexus-compliant thread aware
* logger implementation at any time.
*
* @since 3.0
* @author Kristian Rosenvold
*/
@SuppressWarnings( { "SynchronizationOnLocalVariableOrMethodParameter" } )
public class ThreadOutputMuxer
{
private final Iterator<ProjectSegment> projects;
private final ThreadLocal<ProjectSegment> projectBuildThreadLocal = new ThreadLocal<>();
private final Map<ProjectSegment, ByteArrayOutputStream> streams =
new HashMap<>();
private final Map<ProjectSegment, PrintStream> printStreams = new HashMap<>();
private final ByteArrayOutputStream defaultOutputStreamForUnknownData = new ByteArrayOutputStream();
private final PrintStream defaultPrintStream = new PrintStream( defaultOutputStreamForUnknownData );
private final Set<ProjectSegment> completedBuilds = Collections.synchronizedSet( new HashSet<>() );
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
{
private 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 defaultPrintStream;
}
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
{
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();
}
}
}
}