This is an automated email from the ASF dual-hosted git repository.

mbien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 22459cc10a improved maven indexer failure modes in low space situations
     new 1e03c87c59 Merge pull request #5655 from 
mbien/indexer-no-space-handling
22459cc10a is described below

commit 22459cc10ac87a613a7c230de52bd570329d974d
Author: Michael Bien <[email protected]>
AuthorDate: Sun Mar 12 03:22:01 2023 +0100

    improved maven indexer failure modes in low space situations
    
     - indexing will disable itself if the index failed to extract due
       to no space left on the device (temp or cache) and notify the user
     - take cause and suppressed exceptions into account while scanning
       for "no space" messages
     - added a fallback to clean up remaining files on exception
     - simplified the code a bit
---
 .../maven/indexer/NexusRepositoryIndexerImpl.java  | 136 +++++++++++++--------
 1 file changed, 83 insertions(+), 53 deletions(-)

diff --git 
a/java/maven.indexer/src/org/netbeans/modules/maven/indexer/NexusRepositoryIndexerImpl.java
 
b/java/maven.indexer/src/org/netbeans/modules/maven/indexer/NexusRepositoryIndexerImpl.java
index ec029d90d1..57c410c6d6 100644
--- 
a/java/maven.indexer/src/org/netbeans/modules/maven/indexer/NexusRepositoryIndexerImpl.java
+++ 
b/java/maven.indexer/src/org/netbeans/modules/maven/indexer/NexusRepositoryIndexerImpl.java
@@ -28,22 +28,27 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
-import java.nio.file.FileStore;
+import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
 import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Stream;
 import java.util.zip.ZipError;
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.queryparser.classic.QueryParser;
 import org.codehaus.plexus.PlexusConstants;
 import org.apache.lucene.search.*;
+import org.apache.lucene.store.AlreadyClosedException;
 import org.apache.lucene.store.FSDirectory;
 import org.apache.lucene.store.MMapDirectory;
 import org.apache.maven.artifact.Artifact;
@@ -462,13 +467,18 @@ public class NexusRepositoryIndexerImpl implements 
RepositoryIndexerImplementati
             removeIndexingContext(ic, false);
         }
     }
-    
-    @Messages({"# {0} - folder path",
-               "# {1} - repository name",
-               "MSG_NoSpace=There is not enough space in {0} to download and 
unpack the index for ''{1}''.",
-               "# {0} - folder path",
-               "# {1} - repository name",
-               "MSG_SeemsNoSpace=It seems that there is not enough space in 
{0} to download and unpack the index for ''{1}''."})
+
+    @Messages({"# {0} - repository name",
+               "# {1} - cache path",
+               "# {2} - cache free storage",
+               "# {3} - tmp path",
+               "# {4} - tmp free storage",
+               "MSG_NoSpace="
+                       +"<html>There is not enough space to download and 
unpack the index for ''{0}''.<br/><br/>"
+                       +"''{1}'' has {2} MB free<br/>"
+                       +"''{3}'' has {4} MB free<br/><br/>"
+                       +"Maven indexing is now disabled and can be enabled 
again in the maven settings.</html>",
+    })
     private void indexLoadedRepo(final RepositoryInfo repo, boolean 
updateLocal) throws IOException {
         Mutex mutex = getRepoMutex(repo);
         assert mutex.isWriteAccess();
@@ -523,10 +533,12 @@ public class NexusRepositoryIndexerImpl implements 
RepositoryIndexerImplementati
                         p.setProperty("User-Agent", "netBeans/" + 
System.getProperty("netbeans.buildnumber"));
                         httpwagon.setHttpHeaders(p);
                     }
-                            
+
+                    Path tmpStorage = 
Files.createTempDirectory("index-extraction");
                     ResourceFetcher fetcher = createFetcher(wagon, listener, 
wagonAuth, wagonProxy);
                     listener.setFetcher(fetcher);
                     IndexUpdateRequest iur = new 
IndexUpdateRequest(indexingContext, fetcher);
+                    iur.setIndexTempDir(tmpStorage.toFile());
                     
                     NotifyingIndexCreator nic = null;
                     for (IndexCreator ic : indexingContext.getIndexCreators()) 
{
@@ -543,23 +555,20 @@ public class NexusRepositoryIndexerImpl implements 
RepositoryIndexerImplementati
                         Files.deleteIfExists(getRootGroupCacheFile(repo));
                         remoteIndexUpdater.fetchAndUpdateIndex(iur);
                         storeGroupCache(repo, indexingContext);
-                    } catch (IllegalArgumentException ex) {
-                        // This exception is raised from the maven-indexer.
-                        // maven-indexer supported two formats:
-                        // - legacy/zip: zipped lucene (v2.3) index files
-                        // - gz: maven-indexer specific file format
-                        // The legacy format is no longer supported and when
-                        // the indexer encounters old index files it raises
-                        // this exception
-                        //
-                        // Convert to IOException to utilize the existing error
-                        // handling paths
+                    } catch (IOException | AlreadyClosedException | 
IllegalArgumentException ex) {
+                        // AlreadyClosedException can happen in low storage 
situations when lucene is trying to handle IOEs
+                        // IllegalArgumentException signals remote archive 
format problems
                         fetchFailed = true;
                         throw new IOException("Failed to load maven-index for: 
" + indexingContext.getRepositoryUrl(), ex);
-                    } catch (IOException ex) {
-                        fetchFailed = true;
-                        throw ex;
                     } finally {
+                        if (fetchFailed) {
+                            try{
+                                // make sure no big files remain after 
extraction failure
+                                cleanupDir(tmpStorage);
+                            } catch (IOException ex) {
+                                LOGGER.log(Level.WARNING, "cleanup failed");
+                            }
+                        }
                         if (nic != null) {
                             nic.end();
                         }
@@ -585,45 +594,33 @@ public class NexusRepositoryIndexerImpl implements 
RepositoryIndexerImplementati
                     }
                 }
             }
-        } catch (IOException e) {            
+        } catch (IOException e) {
             if(e.getCause() instanceof ResourceDoesNotExistException) {
                 fireChange(repo, () -> repo.fireNoIndex());
             }
-            File tmpFolder = Places.getCacheDirectory();
-            // see also issue #250365
-            String noSpaceLeftMsg = null;
-            if(e.getMessage() != null && e.getMessage().contains("No space 
left on device")) {
-                noSpaceLeftMsg = 
Bundle.MSG_NoSpace(tmpFolder.getAbsolutePath(), repo.getName());
-            }
-            
-            long downloaded = listener != null ? listener.getUnits() * 1024 : 
-1;
-            long usableSpace = -1;
-            try {
-                FileStore store = Files.getFileStore(tmpFolder.toPath());
-                usableSpace = store.getUsableSpace();                    
-            } catch (IOException ex) {
-                Exceptions.printStackTrace(ex);
-            }
-            LOGGER.log(Level.INFO, "Downloaded maven index file has size {0} 
(zipped). The usable space in {1} is {2}.", new Object[]{downloaded, tmpFolder, 
usableSpace});
+            Path tmpFolder = Paths.get(System.getProperty("java.io.tmpdir"));
+            Path cacheFolder = Places.getCacheDirectory().toPath();
 
-            // still might be a problem with a too small tmp,
-            // let's try to figure out ...
-            if(noSpaceLeftMsg == null && downloaded > -1 && downloaded * 15 > 
usableSpace) {
-                noSpaceLeftMsg = 
Bundle.MSG_SeemsNoSpace(tmpFolder.getAbsolutePath(), repo.getName());
-            }
+            long freeTmpSpace = getFreeSpaceInMB(tmpFolder);
+            long freeCacheSpace = getFreeSpaceInMB(cacheFolder);
+
+            if (isNoSpaceLeftOnDevice(e) || freeCacheSpace < 1000 || 
freeTmpSpace < 1000) {
+
+                long downloaded = listener != null ? listener.getUnits() * 
1024 : -1;
+                LOGGER.log(Level.INFO, "Downloaded maven index file has size 
{0} (zipped). The usable space in {1} is {2} and in {3} MB is {4} MB.",
+                        new Object[] {downloaded, cacheFolder, freeCacheSpace, 
tmpFolder, freeTmpSpace});
+                LOGGER.log(Level.WARNING, "Download/Extraction failed due to 
low storage, indexing is now disabled.", e);
+
+                // disable indexing and tell user about it
+                RepositoryPreferences.setIndexRepositories(false);
 
-            if(noSpaceLeftMsg != null) {
-                LOGGER.log(Level.INFO, null, e);
                 IndexingNotificationProvider np = 
Lookup.getDefault().lookup(IndexingNotificationProvider.class);
                 if(np != null) {
-                    np.notifyError(noSpaceLeftMsg);
-                    unloadIndexingContext(repo.getId());
-                } else {
-                    throw e;
+                    np.notifyError(Bundle.MSG_NoSpace(repo.getName(), 
cacheFolder.toString(), freeCacheSpace, tmpFolder.toString(), freeTmpSpace));
                 }
-            } else {
-                throw e;
+                unloadIndexingContext(repo.getId());
             }
+            throw e;
         } catch (Cancellation x) {
             throw new IOException("canceled indexing", x);
         } catch (ComponentLookupException x) {
@@ -640,6 +637,15 @@ public class NexusRepositoryIndexerImpl implements 
RepositoryIndexerImplementati
         }
     }
 
+    private static boolean isNoSpaceLeftOnDevice(Throwable ex) {
+        String msg = ex.getMessage();
+        Throwable cause = ex.getCause();
+        Throwable[] suppressed = ex.getSuppressed();
+        return (msg != null && msg.contains("No space left on device"))
+            || (cause != null && isNoSpaceLeftOnDevice(cause))
+            || (suppressed.length > 0 && 
Stream.of(suppressed).anyMatch(NexusRepositoryIndexerImpl::isNoSpaceLeftOnDevice));
+    }
+
     private static boolean isDiag() {
         return Boolean.getBoolean("maven.indexing.diag");
     }
@@ -1718,4 +1724,28 @@ public class NexusRepositoryIndexerImpl implements 
RepositoryIndexerImplementati
         }
     }
 
+    private static void cleanupDir(Path path) throws IOException {
+        Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException 
exc) throws IOException {
+                Files.deleteIfExists(dir);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes 
attrs) throws IOException {
+                Files.deleteIfExists(file);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    private long getFreeSpaceInMB(Path path) {
+        try {
+            return Files.getFileStore(path).getUsableSpace() / (1024 * 1024);
+        } catch (IOException ignore) {
+            return -1;
+        }
+    }
+
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to