220 configs = loadPath(in);
221 }
222
223 private static Header load(File f) throws IOException {
224 Header h = new Header(f);
225 h.load();
226 return h;
227 }
228 }
229
230 private final File root;
231 private final File canonicalRoot;
232 private final File parentPath;
233 private final File natlibs;
234 private final File natcmds;
235 private final File configs;
236 private final SimpleLibrary parent;
237 private final Header hd;
238 private final ModuleDictionary moduleDictionary;
239 private final File lockf;
240
241 public String name() { return root.toString(); }
242 public File root() { return canonicalRoot; }
243 public int majorVersion() { return hd.majorVersion; }
244 public int minorVersion() { return hd.minorVersion; }
245 public SimpleLibrary parent() { return parent; }
246 public File natlibs() { return natlibs; }
247 public File natcmds() { return natcmds; }
248 public File configs() { return configs; }
249 public boolean isDeflated() { return hd.isDeflated(); }
250
251 private URI location = null;
252 public URI location() {
253 if (location == null)
254 location = root().toURI();
255 return location;
256 }
257
258 @Override
259 public String toString() {
282 }
283
284 // Opens an existing library
285 private SimpleLibrary(File path) throws IOException {
286 root = path;
287 canonicalRoot = root.getCanonicalFile();
288 Files.ensureIsDirectory(root);
289 hd = Header.load(root);
290
291 parentPath = hd.parent();
292 parent = parentPath != null ? open(parentPath) : null;
293
294 natlibs = hd.natlibs() == null ? null :
295 new File(canonicalRoot, hd.natlibs().toString()).getCanonicalFile();
296 natcmds = hd.natcmds() == null ? null :
297 new File(canonicalRoot, hd.natcmds().toString()).getCanonicalFile();
298 configs = hd.configs() == null ? null :
299 new File(canonicalRoot, hd.configs().toString()).getCanonicalFile();
300
301 lockf = new File(root, FileConstants.META_PREFIX + "lock");
302 moduleDictionary = new ModuleDictionary(root);
303 }
304
305 // Creates a new library
306 private SimpleLibrary(File path, File parentPath, File natlibs, File natcmds,
307 File configs, Set<StorageOption> opts)
308 throws IOException
309 {
310 root = path;
311 canonicalRoot = root.getCanonicalFile();
312 if (root.exists()) {
313 Files.ensureIsDirectory(root);
314 if (root.list().length != 0)
315 throw new IOException(root + ": Already Exists");
316 Files.ensureWriteable(root);
317 } else
318 Files.mkdirs(root, root.toString());
319
320 this.parent = parentPath != null ? open(parentPath) : null;
321 this.parentPath = parentPath != null ? this.parent.root() : null;
322
323 this.natlibs = resolveAndEnsurePath(natlibs);
324 this.natcmds = resolveAndEnsurePath(natcmds);
325 this.configs = resolveAndEnsurePath(configs);
326
327 hd = new Header(canonicalRoot, this.parentPath, relativize(this.natlibs),
328 relativize(this.natcmds), relativize(this.configs), opts);
329 hd.store();
330
331 lockf = new File(root, FileConstants.META_PREFIX + "lock");
332 lockf.createNewFile();
333 moduleDictionary = new ModuleDictionary(canonicalRoot);
334 moduleDictionary.store();
335 }
336
337 public static SimpleLibrary create(File path, File parent, File natlibs,
338 File natcmds, File configs,
339 Set<StorageOption> opts)
340 throws IOException
341 {
342 return new SimpleLibrary(path, parent, natlibs, natcmds, configs, opts);
343 }
344
345 public static SimpleLibrary create(File path, File parent, Set<StorageOption> opts)
346 throws IOException
347 {
348 return new SimpleLibrary(path, parent, null, null, null, opts);
349 }
350
351 public static SimpleLibrary create(File path, File parent)
352 throws IOException
1455 }
1456 } catch (IOException y) {
1457 x.addSuppressed(y);
1458 }
1459 throw x;
1460 } finally {
1461 if (complete) {
1462 moduleDictionary.store();
1463 }
1464 fc.close();
1465 }
1466 }
1467
1468 @Override
1469 public void install(Resolution res, boolean verifySignature)
1470 throws ConfigurationException, IOException, SignatureException
1471 {
1472 install(res, verifySignature, false);
1473 }
1474
1475 /**
1476 * <p> Pre-install one or more modules to an arbitrary destination
1477 * directory. </p>
1478 *
1479 * <p> A pre-installed module has the same format as within the library
1480 * itself, except that there is never a configuration file. </p>
1481 *
1482 * <p> This method is provided for use by the module-packaging tool. </p>
1483 *
1484 * @param mfs
1485 * The manifest describing the contents of the modules to be
1486 * pre-installed
1487 *
1488 * @param dst
1489 * The destination directory, with one subdirectory per module
1490 * name, each of which contains one subdirectory per version
1491 */
1492 public void preInstall(Collection<Manifest> mfs, File dst)
1493 throws IOException
1494 {
1525 * library. </p>
1526 *
1527 * @param mids
1528 * The module ids of the new or updated modules, or
1529 * {@code null} if the configuration of every root module
1530 * should be (re)computed
1531 */
1532 public void configure(Collection<ModuleId> mids)
1533 throws ConfigurationException, IOException
1534 {
1535 try (FileChannel fc = FileChannel.open(lockf.toPath(), WRITE)) {
1536 fc.lock();
1537 configureWhileModuleDirectoryLocked(mids);
1538 }
1539 }
1540
1541 private void configureWhileModuleDirectoryLocked(Collection<ModuleId> mids)
1542 throws ConfigurationException, IOException
1543 {
1544 // ## mids not used yet
1545 List<ModuleId> roots = new ArrayList<>();
1546 for (ModuleId mid : listLocalDeclaringModuleIds()) {
1547 // each module can have multiple entry points
1548 // only configure once for each module.
1549 ModuleInfo mi = readModuleInfo(mid);
1550 for (ModuleView mv : mi.views()) {
1551 if (mv.mainClass() != null) {
1552 roots.add(mid);
1553 break;
1554 }
1555 }
1556 }
1557
1558 for (ModuleId mid : roots) {
1559 // ## We could be a lot more clever about this!
1560 Configuration<Context> cf
1561 = Configurator.configure(this, mid.toQuery());
1562 File md = moduleDictionary.findDeclaringModuleDir(mid);
1563 new StoredConfiguration(md, cf).store();
1564 }
1565 }
1566
1567 public URI findLocalResource(ModuleId mid, String name)
1568 throws IOException
1569 {
1570 return locateContent(mid, name);
1571 }
1572
1573 public File findLocalNativeLibrary(ModuleId mid, String name)
1574 throws IOException
1575 {
1576 File f = natlibs();
1577 if (f == null) {
1578 f = moduleDictionary.findDeclaringModuleDir(mid);
1579 if (f == null)
1580 return null;
1581 f = new File(f, "lib");
1582 }
1583 f = new File(f, name);
1584 if (!f.exists())
1585 return null;
1781 providingModuleIds.remove(view.id());
1782 Set<ModuleId> mids = moduleIdsForName.get(view.id().name());
1783 if (mids != null)
1784 mids.remove(view.id());
1785 for (ModuleId alias : view.aliases()) {
1786 providingModuleIds.remove(alias);
1787 mids = moduleIdsForName.get(alias.name());
1788 if (mids != null)
1789 mids.remove(view.id());
1790 }
1791 }
1792 File md = moduleDir(root, mi.id());
1793 delete(md);
1794 }
1795
1796 private void delete(File md) throws IOException {
1797 if (!md.exists())
1798 return;
1799
1800 checkModuleDir(md);
1801 ModuleFile.Reader.remove(md);
1802 File parent = md.getParentFile();
1803 if (parent.list().length == 0)
1804 parent.delete();
1805 }
1806
1807 void refresh() throws IOException {
1808 providingModuleIds = new LinkedHashMap<>();
1809 moduleIdsForName = new LinkedHashMap<>();
1810 modules = new HashSet<>();
1811
1812 try (DirectoryStream<Path> ds = java.nio.file.Files.newDirectoryStream(root.toPath())) {
1813 for (Path mnp : ds) {
1814 String mn = mnp.toFile().getName();
1815 if (mn.startsWith(FileConstants.META_PREFIX)) {
1816 continue;
1817 }
1818
1819 try (DirectoryStream<Path> mds = java.nio.file.Files.newDirectoryStream(mnp)) {
1820 for (Path versionp : mds) {
1821 File v = versionp.toFile();
1822 if (!v.isDirectory()) {
1823 throw new IOException(versionp + ": Not a directory");
1824 }
1825 modules.add(jms.parseModuleId(mn, v.getName()));
1826 }
1827 }
1828 }
1829 }
1830 for (ModuleId mid : modules) {
1831 byte[] bs = Files.load(new File(moduleDir(root, mid), "info"));
1832 ModuleInfo mi = jms.parseModuleInfo(bs);
1833 addToDirectory(mi);
1834 }
1835 }
|
220 configs = loadPath(in);
221 }
222
223 private static Header load(File f) throws IOException {
224 Header h = new Header(f);
225 h.load();
226 return h;
227 }
228 }
229
230 private final File root;
231 private final File canonicalRoot;
232 private final File parentPath;
233 private final File natlibs;
234 private final File natcmds;
235 private final File configs;
236 private final SimpleLibrary parent;
237 private final Header hd;
238 private final ModuleDictionary moduleDictionary;
239 private final File lockf;
240 private final File trash;
241
242 public String name() { return root.toString(); }
243 public File root() { return canonicalRoot; }
244 public int majorVersion() { return hd.majorVersion; }
245 public int minorVersion() { return hd.minorVersion; }
246 public SimpleLibrary parent() { return parent; }
247 public File natlibs() { return natlibs; }
248 public File natcmds() { return natcmds; }
249 public File configs() { return configs; }
250 public boolean isDeflated() { return hd.isDeflated(); }
251
252 private URI location = null;
253 public URI location() {
254 if (location == null)
255 location = root().toURI();
256 return location;
257 }
258
259 @Override
260 public String toString() {
283 }
284
285 // Opens an existing library
286 private SimpleLibrary(File path) throws IOException {
287 root = path;
288 canonicalRoot = root.getCanonicalFile();
289 Files.ensureIsDirectory(root);
290 hd = Header.load(root);
291
292 parentPath = hd.parent();
293 parent = parentPath != null ? open(parentPath) : null;
294
295 natlibs = hd.natlibs() == null ? null :
296 new File(canonicalRoot, hd.natlibs().toString()).getCanonicalFile();
297 natcmds = hd.natcmds() == null ? null :
298 new File(canonicalRoot, hd.natcmds().toString()).getCanonicalFile();
299 configs = hd.configs() == null ? null :
300 new File(canonicalRoot, hd.configs().toString()).getCanonicalFile();
301
302 lockf = new File(root, FileConstants.META_PREFIX + "lock");
303 trash = new File(root, TRASH);
304 moduleDictionary = new ModuleDictionary(root);
305 }
306
307 // Creates a new library
308 private SimpleLibrary(File path, File parentPath, File natlibs, File natcmds,
309 File configs, Set<StorageOption> opts)
310 throws IOException
311 {
312 root = path;
313 canonicalRoot = root.getCanonicalFile();
314 if (root.exists()) {
315 Files.ensureIsDirectory(root);
316 if (root.list().length != 0)
317 throw new IOException(root + ": Already Exists");
318 Files.ensureWriteable(root);
319 } else
320 Files.mkdirs(root, root.toString());
321
322 this.parent = parentPath != null ? open(parentPath) : null;
323 this.parentPath = parentPath != null ? this.parent.root() : null;
324
325 this.natlibs = resolveAndEnsurePath(natlibs);
326 this.natcmds = resolveAndEnsurePath(natcmds);
327 this.configs = resolveAndEnsurePath(configs);
328
329 hd = new Header(canonicalRoot, this.parentPath, relativize(this.natlibs),
330 relativize(this.natcmds), relativize(this.configs), opts);
331 hd.store();
332
333 lockf = new File(root, FileConstants.META_PREFIX + "lock");
334 lockf.createNewFile();
335 trash = new File(root, TRASH);
336 Files.mkdirs(trash, "module library trash");
337 moduleDictionary = new ModuleDictionary(canonicalRoot);
338 moduleDictionary.store();
339 }
340
341 public static SimpleLibrary create(File path, File parent, File natlibs,
342 File natcmds, File configs,
343 Set<StorageOption> opts)
344 throws IOException
345 {
346 return new SimpleLibrary(path, parent, natlibs, natcmds, configs, opts);
347 }
348
349 public static SimpleLibrary create(File path, File parent, Set<StorageOption> opts)
350 throws IOException
351 {
352 return new SimpleLibrary(path, parent, null, null, null, opts);
353 }
354
355 public static SimpleLibrary create(File path, File parent)
356 throws IOException
1459 }
1460 } catch (IOException y) {
1461 x.addSuppressed(y);
1462 }
1463 throw x;
1464 } finally {
1465 if (complete) {
1466 moduleDictionary.store();
1467 }
1468 fc.close();
1469 }
1470 }
1471
1472 @Override
1473 public void install(Resolution res, boolean verifySignature)
1474 throws ConfigurationException, IOException, SignatureException
1475 {
1476 install(res, verifySignature, false);
1477 }
1478
1479 @Override
1480 public void removeForcibly(List<ModuleId> mids)
1481 throws IOException
1482 {
1483 try {
1484 remove(mids, true, false);
1485 } catch (ConfigurationException x) {
1486 throw new Error("should not be thrown when forcibly removing", x);
1487 }
1488 }
1489
1490 @Override
1491 public void remove(List<ModuleId> mids, boolean dry)
1492 throws ConfigurationException, IOException
1493 {
1494 remove(mids, false, dry);
1495 }
1496
1497 private void remove(List<ModuleId> mids, boolean force, boolean dry)
1498 throws ConfigurationException, IOException
1499 {
1500 IOException ioe = null;
1501 FileChannel fc = FileChannel.open(lockf.toPath(), WRITE);
1502 try {
1503 fc.lock();
1504 for (ModuleId mid : mids) {
1505 if (moduleDictionary.findDeclaringModuleDir(mid) == null)
1506 throw new IllegalArgumentException(mid + ": No such module");
1507 }
1508 if (!force)
1509 checkRootsRequire(mids);
1510 if (dry)
1511 return;
1512
1513 // The library may be altered after this point, so the modules
1514 // dictionary needs to be refreshed
1515 List<IOException> excs = removeWhileLocked(mids);
1516 try {
1517 moduleDictionary.refresh();
1518 moduleDictionary.store();
1519 } catch (IOException x) {
1520 excs.add(x);
1521 }
1522 if (!excs.isEmpty()) {
1523 ioe = excs.remove(0);
1524 for (IOException x : excs)
1525 ioe.addSuppressed(x);
1526 }
1527 } catch (IOException x) {
1528 ioe = x;
1529 } finally {
1530 fc.close();
1531 if (ioe != null)
1532 throw ioe;
1533 }
1534 }
1535
1536 private void checkRootsRequire(List<ModuleId> mids)
1537 throws ConfigurationException, IOException
1538 {
1539 // ## We do not know if a root module in a child library depends on one
1540 // ## of the 'to be removed' modules. We would break it's configuration.
1541
1542 // check each root configuration for reference to a module in mids
1543 for (ModuleId rootid : libraryRoots()) {
1544 // skip any root modules being removed
1545 if (mids.contains(rootid))
1546 continue;
1547
1548 Configuration<Context> cf = readConfiguration(rootid);
1549 for (Context cx : cf.contexts()) {
1550 for (ModuleId mid : cx.modules()) {
1551 if (mids.contains(mid))
1552 throw new ConfigurationException(mid +
1553 ": being used by " + rootid);
1554 }
1555 }
1556 }
1557 }
1558
1559 private static final String TRASH = ".trash";
1560 // file name generation, same as java.io.File for now
1561 // lazy initialization of SecureRandom
1562 private static class LazyInitialization {
1563 static final SecureRandom random = new SecureRandom();
1564 }
1565 private static File moduleTrashDir(File trash, ModuleId mid)
1566 throws IOException
1567 {
1568 File mtd = null;
1569 for (;;) {
1570 long n = LazyInitialization.random.nextLong();
1571 n = (n == Long.MIN_VALUE) ? 0 : Math.abs(n);
1572 Version version = mid.version();
1573 String v = (version != null) ? version.toString() : "default";
1574 String modTrashName = mid.name() + '_' + v + '_' + Long.toString(n);
1575 mtd = new File(trash, modTrashName);
1576 if (!mtd.exists())
1577 break;
1578 }
1579 Files.mkdirs(mtd, mtd.toString());
1580 return mtd;
1581 }
1582
1583 private List<IOException> removeWhileLocked(List<ModuleId> mids) {
1584 List<IOException> excs = new ArrayList<>();
1585 // First move the modules to the .trash dir
1586 for (ModuleId mid : mids) {
1587 try {
1588 File mtd = moduleTrashDir(trash, mid);
1589 File md = moduleDir(root, mid);
1590 java.nio.file.Files.move(md.toPath(), mtd.toPath(), ATOMIC_MOVE);
1591 File p = md.getParentFile();
1592 if (p.list().length == 0)
1593 java.nio.file.Files.delete(p.toPath());
1594 } catch (IOException x) {
1595 excs.add(x);
1596 }
1597 }
1598 for (String tm : trash.list())
1599 excs.addAll(ModuleFile.Reader.remove(new File(trash, tm)));
1600
1601 return excs;
1602 }
1603
1604 /**
1605 * <p> Pre-install one or more modules to an arbitrary destination
1606 * directory. </p>
1607 *
1608 * <p> A pre-installed module has the same format as within the library
1609 * itself, except that there is never a configuration file. </p>
1610 *
1611 * <p> This method is provided for use by the module-packaging tool. </p>
1612 *
1613 * @param mfs
1614 * The manifest describing the contents of the modules to be
1615 * pre-installed
1616 *
1617 * @param dst
1618 * The destination directory, with one subdirectory per module
1619 * name, each of which contains one subdirectory per version
1620 */
1621 public void preInstall(Collection<Manifest> mfs, File dst)
1622 throws IOException
1623 {
1654 * library. </p>
1655 *
1656 * @param mids
1657 * The module ids of the new or updated modules, or
1658 * {@code null} if the configuration of every root module
1659 * should be (re)computed
1660 */
1661 public void configure(Collection<ModuleId> mids)
1662 throws ConfigurationException, IOException
1663 {
1664 try (FileChannel fc = FileChannel.open(lockf.toPath(), WRITE)) {
1665 fc.lock();
1666 configureWhileModuleDirectoryLocked(mids);
1667 }
1668 }
1669
1670 private void configureWhileModuleDirectoryLocked(Collection<ModuleId> mids)
1671 throws ConfigurationException, IOException
1672 {
1673 // ## mids not used yet
1674 for (ModuleId mid : libraryRoots()) {
1675 // ## We could be a lot more clever about this!
1676 Configuration<Context> cf
1677 = Configurator.configure(this, mid.toQuery());
1678 File md = moduleDictionary.findDeclaringModuleDir(mid);
1679 new StoredConfiguration(md, cf).store();
1680 }
1681 }
1682
1683 private List<ModuleId> libraryRoots()
1684 throws IOException
1685 {
1686 List<ModuleId> roots = new ArrayList<>();
1687 for (ModuleId mid : listLocalDeclaringModuleIds()) {
1688 // each module can have multiple entry points, but
1689 // only one configuration for each module.
1690 ModuleInfo mi = readModuleInfo(mid);
1691 for (ModuleView mv : mi.views()) {
1692 if (mv.mainClass() != null) {
1693 roots.add(mid);
1694 break;
1695 }
1696 }
1697 }
1698 return roots;
1699 }
1700
1701 public URI findLocalResource(ModuleId mid, String name)
1702 throws IOException
1703 {
1704 return locateContent(mid, name);
1705 }
1706
1707 public File findLocalNativeLibrary(ModuleId mid, String name)
1708 throws IOException
1709 {
1710 File f = natlibs();
1711 if (f == null) {
1712 f = moduleDictionary.findDeclaringModuleDir(mid);
1713 if (f == null)
1714 return null;
1715 f = new File(f, "lib");
1716 }
1717 f = new File(f, name);
1718 if (!f.exists())
1719 return null;
1915 providingModuleIds.remove(view.id());
1916 Set<ModuleId> mids = moduleIdsForName.get(view.id().name());
1917 if (mids != null)
1918 mids.remove(view.id());
1919 for (ModuleId alias : view.aliases()) {
1920 providingModuleIds.remove(alias);
1921 mids = moduleIdsForName.get(alias.name());
1922 if (mids != null)
1923 mids.remove(view.id());
1924 }
1925 }
1926 File md = moduleDir(root, mi.id());
1927 delete(md);
1928 }
1929
1930 private void delete(File md) throws IOException {
1931 if (!md.exists())
1932 return;
1933
1934 checkModuleDir(md);
1935 // ## TODO:
1936 ModuleFile.Reader.remove(md);
1937 File parent = md.getParentFile();
1938 if (parent.list().length == 0)
1939 parent.delete();
1940 }
1941
1942 void refresh() throws IOException {
1943 providingModuleIds = new LinkedHashMap<>();
1944 moduleIdsForName = new LinkedHashMap<>();
1945 modules = new HashSet<>();
1946
1947 try (DirectoryStream<Path> ds = java.nio.file.Files.newDirectoryStream(root.toPath())) {
1948 for (Path mnp : ds) {
1949 String mn = mnp.toFile().getName();
1950 if (mn.startsWith(FileConstants.META_PREFIX) ||
1951 TRASH.equals(mn)) {
1952 continue;
1953 }
1954
1955 try (DirectoryStream<Path> mds = java.nio.file.Files.newDirectoryStream(mnp)) {
1956 for (Path versionp : mds) {
1957 File v = versionp.toFile();
1958 if (!v.isDirectory()) {
1959 throw new IOException(versionp + ": Not a directory");
1960 }
1961 modules.add(jms.parseModuleId(mn, v.getName()));
1962 }
1963 }
1964 }
1965 }
1966 for (ModuleId mid : modules) {
1967 byte[] bs = Files.load(new File(moduleDir(root, mid), "info"));
1968 ModuleInfo mi = jms.parseModuleInfo(bs);
1969 addToDirectory(mi);
1970 }
1971 }
|