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


Reply via email to