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 }