atlib77 commented on a change in pull request #6: URL: https://github.com/apache/maven-clean-plugin/pull/6#discussion_r771974967
########## File path: src/main/java/org/apache/maven/plugins/clean/Cleaner.java ########## @@ -286,4 +393,189 @@ public void update( Result result ) } + static class BackgroundCleaner extends Thread + { + + private static BackgroundCleaner instance; + + private final Deque<File> filesToDelete = new ArrayDeque<>(); + + private final Cleaner cleaner; + + private static final int NEW = 0; + private static final int RUNNING = 1; + private static final int STOPPED = 2; + + private int status = NEW; + + public static void delete( Cleaner cleaner, File dir ) + { + synchronized ( BackgroundCleaner.class ) + { + if ( instance == null || !instance.doDelete( dir ) ) + { + instance = new BackgroundCleaner( cleaner, dir ); + } + } + } + + static void sessionEnd() + { + synchronized ( BackgroundCleaner.class ) + { + if ( instance != null ) + { + instance.doSessionEnd(); + } + } + } + + private BackgroundCleaner( Cleaner cleaner, File dir ) + { + this.cleaner = cleaner; + init( cleaner.fastDir, dir ); + } + + public void run() + { + while ( true ) + { + File basedir = pollNext(); + if ( basedir == null ) + { + break; + } + try + { + cleaner.delete( basedir, "", null, false, false, true ); + } + catch ( IOException e ) + { + // do not display errors + } + } + } + + synchronized void init( File fastDir, File dir ) + { + if ( fastDir.isDirectory() ) + { + File[] children = fastDir.listFiles(); + if ( children != null && children.length > 0 ) + { + for ( File child : children ) + { + doDelete( child ); + } + } + } + doDelete( dir ); + } + + synchronized File pollNext() + { + File basedir = filesToDelete.poll(); + if ( basedir == null ) + { + if ( cleaner.session != null ) + { + SessionData data = cleaner.session.getRepositorySession().getData(); + File lastDir = ( File ) data.get( LAST_DIRECTORY_TO_DELETE ); + if ( lastDir != null ) + { + data.set( LAST_DIRECTORY_TO_DELETE, null ); + return lastDir; + } + } + status = STOPPED; + notifyAll(); + } + return basedir; + } + + synchronized boolean doDelete( File dir ) + { + if ( status == STOPPED ) + { + return false; + } + filesToDelete.add( dir ); + if ( status == NEW ) + { + status = RUNNING; + notifyAll(); + wrapExecutionListener(); + start(); + } + return true; + } + + /** + * If this has not been done already, we wrap the ExecutionListener inside a proxy + * which simply delegates call to the previous listener. When the session ends, it will + * also call {@link BackgroundCleaner#sessionEnd()}. + * There's no clean API to do that properly as this is a very unusual use case for a plugin + * to outlive its main execution. + */ + private void wrapExecutionListener() + { + ExecutionListener executionListener = cleaner.session.getRequest().getExecutionListener(); + if ( executionListener == null + || !Proxy.isProxyClass( executionListener.getClass() ) + || !( Proxy.getInvocationHandler( executionListener ) instanceof SpyInvocationHandler ) ) + { + ExecutionListener listener = ( ExecutionListener ) Proxy.newProxyInstance( + ExecutionListener.class.getClassLoader(), + new Class[] { ExecutionListener.class }, + new SpyInvocationHandler( executionListener ) ); + cleaner.session.getRequest().setExecutionListener( listener ); + } + } + + synchronized void doSessionEnd() + { + if ( status != STOPPED ) + { + try + { + cleaner.logInfo.log( "Waiting for background file deletion" ); + while ( status != STOPPED ) + { + wait(); + } + } + catch ( InterruptedException e ) + { + // ignore + } + } + } + + } + + static class SpyInvocationHandler implements InvocationHandler + { + private final ExecutionListener delegate; + + SpyInvocationHandler( ExecutionListener delegate ) + { + this.delegate = delegate; + } + + @Override + public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable + { + if ( "sessionEnded".equals( method.getName() ) ) + { + BackgroundCleaner.sessionEnd(); Review comment: For my use case, it would be optimal if we could have e.g. a few different modes of when and how the delete takes place. Something like: ``` enum Mode { /* Start immediately deleting in fastmode, deleting takes place during build and completes at session end (blocks) */ background, /* Deletes in fastmode at the end of the session (blocks) */ atEnd, /* Start immediately deleting in fastmode, deleting takes place during build, ignores session end. Only applicable when running in a daemon, otherwise the delete might not complete */ backgroundDaemon, /* Starts deleting in fastmode at session end. Only applicable when running in a daemon, otherwise the delete might not complete */ atEndDaemon } ``` Also, I have noticed that running the delete in multiple threads does indeed speed things up so perhaps have that option as well (another property). I know the daemon mode doesn't make sense for **mvn** but it would mean the terminal would be freed up a lot sooner when running with **mvn-daemon**. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@maven.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org