src/share/classes/org/openjdk/jigsaw/SimpleLibrary.java

Print this page

        

@@ -235,10 +235,11 @@
     private final File configs;
     private final SimpleLibrary parent;
     private final Header hd;
     private final ModuleDictionary moduleDictionary;
     private final File lockf;
+    private final File trash;
 
     public String name() { return root.toString(); }
     public File root() { return canonicalRoot; }
     public int majorVersion() { return hd.majorVersion; }
     public int minorVersion() { return hd.minorVersion; }

@@ -297,10 +298,11 @@
             new File(canonicalRoot, hd.natcmds().toString()).getCanonicalFile();
         configs = hd.configs() == null ? null :
             new File(canonicalRoot, hd.configs().toString()).getCanonicalFile();
 
         lockf = new File(root, FileConstants.META_PREFIX + "lock");
+        trash = new File(root, TRASH);
         moduleDictionary = new ModuleDictionary(root);
     }
 
     // Creates a new library
     private SimpleLibrary(File path, File parentPath, File natlibs, File natcmds,

@@ -328,10 +330,12 @@
                         relativize(this.natcmds), relativize(this.configs), opts);
         hd.store();
 
         lockf = new File(root, FileConstants.META_PREFIX + "lock");
         lockf.createNewFile();
+        trash = new File(root, TRASH);
+        Files.mkdirs(trash, "module library trash");
         moduleDictionary = new ModuleDictionary(canonicalRoot);
         moduleDictionary.store();
     }
 
     public static SimpleLibrary create(File path, File parent, File natlibs,

@@ -1470,10 +1474,135 @@
         throws ConfigurationException, IOException, SignatureException
     {
         install(res, verifySignature, false);
     }
 
+    @Override
+    public void removeForcibly(List<ModuleId> mids)
+        throws IOException
+    {
+        try {
+            remove(mids, true, false);
+        } catch (ConfigurationException x) {
+            throw new Error("should not be thrown when forcibly removing", x);
+        }
+    }
+
+    @Override
+    public void remove(List<ModuleId> mids, boolean dry)
+        throws ConfigurationException, IOException
+    {
+        remove(mids, false,  dry);
+    }
+
+    private void remove(List<ModuleId> mids, boolean force, boolean dry)
+        throws ConfigurationException, IOException
+    {
+        IOException ioe = null;
+        FileChannel fc = FileChannel.open(lockf.toPath(), WRITE);
+        try {
+            fc.lock();
+            for (ModuleId mid : mids) {
+                if (moduleDictionary.findDeclaringModuleDir(mid) == null)
+                    throw new IllegalArgumentException(mid + ": No such module");
+            }
+            if (!force)
+                checkRootsRequire(mids);
+            if (dry)
+                return;
+
+            // The library may be altered after this point, so the modules
+            // dictionary needs to be refreshed
+            List<IOException> excs = removeWhileLocked(mids);
+            try {
+                moduleDictionary.refresh();
+                moduleDictionary.store();
+            } catch (IOException x) {
+                excs.add(x);
+            }
+            if (!excs.isEmpty()) {
+                ioe = excs.remove(0);
+                for (IOException x : excs)
+                    ioe.addSuppressed(x);
+            }
+        } catch (IOException x) {
+            ioe = x;
+        } finally {
+            fc.close();
+            if (ioe != null)
+                throw ioe;
+        }
+    }
+
+    private void checkRootsRequire(List<ModuleId> mids)
+        throws ConfigurationException, IOException
+    {
+        // ## We do not know if a root module in a child library depends on one
+        // ## of the 'to be removed' modules. We would break it's configuration.
+
+        // check each root configuration for reference to a module in mids
+        for (ModuleId rootid : libraryRoots()) {
+            // skip any root modules being removed
+            if (mids.contains(rootid))
+                continue;
+
+            Configuration<Context> cf = readConfiguration(rootid);
+            for (Context cx : cf.contexts()) {
+                for (ModuleId mid : cx.modules()) {
+                    if (mids.contains(mid))
+                        throw new ConfigurationException(mid +
+                                ": being used by " + rootid);
+                }
+            }
+        }
+    }
+
+    private static final String TRASH = ".trash";
+    // file name generation, same as java.io.File for now
+    // lazy initialization of SecureRandom
+    private static class LazyInitialization {
+        static final SecureRandom random = new SecureRandom(); 
+    }
+    private static File moduleTrashDir(File trash, ModuleId mid)
+        throws IOException
+    {
+        File mtd = null;
+        for (;;) {
+            long n = LazyInitialization.random.nextLong();
+            n = (n == Long.MIN_VALUE) ? 0 : Math.abs(n);
+            Version version = mid.version();
+            String v = (version != null) ? version.toString() : "default";
+            String modTrashName = mid.name() + '_' + v + '_' + Long.toString(n);
+            mtd = new File(trash, modTrashName);
+            if (!mtd.exists())
+                break;
+        }
+        Files.mkdirs(mtd, mtd.toString());
+        return mtd;
+    }
+
+    private List<IOException> removeWhileLocked(List<ModuleId> mids) {
+        List<IOException> excs = new ArrayList<>();
+        // First move the modules to the .trash dir
+        for (ModuleId mid : mids) {
+            try {
+                File mtd = moduleTrashDir(trash, mid);
+                File md = moduleDir(root, mid);
+                java.nio.file.Files.move(md.toPath(), mtd.toPath(), ATOMIC_MOVE);
+                File p = md.getParentFile();
+                if (p.list().length == 0)
+                    java.nio.file.Files.delete(p.toPath());
+            } catch (IOException x) {
+                excs.add(x);
+            }
+        }
+        for (String tm : trash.list())
+            excs.addAll(ModuleFile.Reader.remove(new File(trash, tm)));
+        
+        return excs;
+    }
+
     /**
      * <p> Pre-install one or more modules to an arbitrary destination
      * directory. </p>
      *
      * <p> A pre-installed module has the same format as within the library

@@ -1540,31 +1669,36 @@
 
     private void configureWhileModuleDirectoryLocked(Collection<ModuleId> mids)
         throws ConfigurationException, IOException
     {
         // ## mids not used yet
+        for (ModuleId mid : libraryRoots()) {
+            // ## We could be a lot more clever about this!
+            Configuration<Context> cf
+                = Configurator.configure(this, mid.toQuery());
+            File md = moduleDictionary.findDeclaringModuleDir(mid);
+            new StoredConfiguration(md, cf).store();
+        }
+    }
+
+    private List<ModuleId> libraryRoots()
+        throws IOException
+    {
         List<ModuleId> roots = new ArrayList<>();
         for (ModuleId mid : listLocalDeclaringModuleIds()) {
-            // each module can have multiple entry points
-            // only configure once for each module.
+            // each module can have multiple entry points, but
+            // only one configuration for each module.
             ModuleInfo mi = readModuleInfo(mid);
             for (ModuleView mv : mi.views()) {
                 if (mv.mainClass() != null) {
                     roots.add(mid);
                     break;
                 }
             }
         }
-
-        for (ModuleId mid : roots) {
-            // ## We could be a lot more clever about this!
-            Configuration<Context> cf
-                = Configurator.configure(this, mid.toQuery());
-            File md = moduleDictionary.findDeclaringModuleDir(mid);
-            new StoredConfiguration(md, cf).store();
+        return roots;
         }
-    }
 
     public URI findLocalResource(ModuleId mid, String name)
         throws IOException
     {
         return locateContent(mid, name);

@@ -1796,10 +1930,11 @@
         private void delete(File md) throws IOException {
             if (!md.exists())
                 return;
 
             checkModuleDir(md);
+            // ## TODO:
             ModuleFile.Reader.remove(md);
             File parent = md.getParentFile();
             if (parent.list().length == 0)
                 parent.delete();
         }

@@ -1810,11 +1945,12 @@
             modules = new HashSet<>();
 
             try (DirectoryStream<Path> ds = java.nio.file.Files.newDirectoryStream(root.toPath())) {
                 for (Path mnp : ds) {
                     String mn = mnp.toFile().getName();
-                    if (mn.startsWith(FileConstants.META_PREFIX)) {
+                    if (mn.startsWith(FileConstants.META_PREFIX) ||
+                        TRASH.equals(mn)) {
                         continue;
                     }
 
                     try (DirectoryStream<Path> mds = java.nio.file.Files.newDirectoryStream(mnp)) {
                         for (Path versionp : mds) {