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;
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
1502 try (FileChannel fc = FileChannel.open(lockf.toPath(), WRITE)) {
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 ensureNotInConfiguration(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 } finally {
1528 if (ioe != null)
1529 throw ioe;
1530 }
1531 }
1532
1533 private void ensureNotInConfiguration(List<ModuleId> mids)
1534 throws ConfigurationException, IOException
1535 {
1536 // ## We do not know if a root module in a child library depends on one
1537 // ## of the 'to be removed' modules. We would break it's configuration.
1538
1539 // check each root configuration for reference to a module in mids
1540 for (ModuleId rootid : libraryRoots()) {
1541 // skip any root modules being removed
1542 if (mids.contains(rootid))
1543 continue;
1544
1545 Configuration<Context> cf = readConfiguration(rootid);
1546 for (Context cx : cf.contexts()) {
1547 for (ModuleId mid : cx.modules()) {
1548 if (mids.contains(mid))
1549 throw new ConfigurationException(mid +
1550 ": being used by " + rootid);
1551 }
1552 }
1553 }
1554 }
1555
1556 private static final String TRASH = ".trash";
1557 // lazy initialization of Random
1558 private static class LazyInitialization {
1559 static final Random random = new Random();
1560 }
1561 private static Path moduleTrashDir(File trash, ModuleId mid)
1562 throws IOException
1563 {
1564 String mn = mid.name();
1565 Version version = mid.version();
1566 String v = (version != null) ? version.toString() : "default";
1567 for (;;) {
1568 long n = LazyInitialization.random.nextLong();
1569 n = (n == Long.MIN_VALUE) ? 0 : Math.abs(n);
1570 String modTrashName = mn + '_' + v + '_' + Long.toString(n);
1571 File mtd = new File(trash, modTrashName);
1572 if (!mtd.exists())
1573 return mtd.toPath();
1574 }
1575 }
1576
1577 private List<IOException> removeWhileLocked(List<ModuleId> mids) {
1578 List<IOException> excs = new ArrayList<>();
1579 // First move the modules to the .trash dir
1580 for (ModuleId mid : mids) {
1581 try {
1582 File md = moduleDir(root, mid);
1583 java.nio.file.Files.move(md.toPath(),
1584 moduleTrashDir(trash, mid),
1585 ATOMIC_MOVE);
1586 File p = md.getParentFile();
1587 if (p.list().length == 0)
1588 java.nio.file.Files.delete(p.toPath());
1589 } catch (IOException x) {
1590 excs.add(x);
1591 }
1592 }
1593 for (String tm : trash.list())
1594 excs.addAll(ModuleFile.Reader.remove(new File(trash, tm)));
1595
1596 return excs;
1597 }
1598
1599 /**
1600 * <p> Pre-install one or more modules to an arbitrary destination
1601 * directory. </p>
1602 *
1603 * <p> A pre-installed module has the same format as within the library
1604 * itself, except that there is never a configuration file. </p>
1605 *
1606 * <p> This method is provided for use by the module-packaging tool. </p>
1607 *
1608 * @param mfs
1609 * The manifest describing the contents of the modules to be
1610 * pre-installed
1611 *
1612 * @param dst
1613 * The destination directory, with one subdirectory per module
1614 * name, each of which contains one subdirectory per version
1615 */
1616 public void preInstall(Collection<Manifest> mfs, File dst)
1617 throws IOException
1618 {
1649 * library. </p>
1650 *
1651 * @param mids
1652 * The module ids of the new or updated modules, or
1653 * {@code null} if the configuration of every root module
1654 * should be (re)computed
1655 */
1656 public void configure(Collection<ModuleId> mids)
1657 throws ConfigurationException, IOException
1658 {
1659 try (FileChannel fc = FileChannel.open(lockf.toPath(), WRITE)) {
1660 fc.lock();
1661 configureWhileModuleDirectoryLocked(mids);
1662 }
1663 }
1664
1665 private void configureWhileModuleDirectoryLocked(Collection<ModuleId> mids)
1666 throws ConfigurationException, IOException
1667 {
1668 // ## mids not used yet
1669 for (ModuleId mid : libraryRoots()) {
1670 // ## We could be a lot more clever about this!
1671 Configuration<Context> cf
1672 = Configurator.configure(this, mid.toQuery());
1673 File md = moduleDictionary.findDeclaringModuleDir(mid);
1674 new StoredConfiguration(md, cf).store();
1675 }
1676 }
1677
1678 private List<ModuleId> libraryRoots()
1679 throws IOException
1680 {
1681 List<ModuleId> roots = new ArrayList<>();
1682 for (ModuleId mid : listLocalDeclaringModuleIds()) {
1683 // each module can have multiple entry points, but
1684 // only one configuration for each module.
1685 ModuleInfo mi = readModuleInfo(mid);
1686 for (ModuleView mv : mi.views()) {
1687 if (mv.mainClass() != null) {
1688 roots.add(mid);
1689 break;
1690 }
1691 }
1692 }
1693 return roots;
1694 }
1695
1696 public URI findLocalResource(ModuleId mid, String name)
1697 throws IOException
1698 {
1699 return locateContent(mid, name);
1700 }
1701
1702 public File findLocalNativeLibrary(ModuleId mid, String name)
1703 throws IOException
1704 {
1705 File f = natlibs();
1706 if (f == null) {
1707 f = moduleDictionary.findDeclaringModuleDir(mid);
1708 if (f == null)
1709 return null;
1710 f = new File(f, "lib");
1711 }
1712 f = new File(f, name);
1713 if (!f.exists())
1714 return null;
1924
1925 private void delete(File md) throws IOException {
1926 if (!md.exists())
1927 return;
1928
1929 checkModuleDir(md);
1930 ModuleFile.Reader.remove(md);
1931 File parent = md.getParentFile();
1932 if (parent.list().length == 0)
1933 parent.delete();
1934 }
1935
1936 void refresh() throws IOException {
1937 providingModuleIds = new LinkedHashMap<>();
1938 moduleIdsForName = new LinkedHashMap<>();
1939 modules = new HashSet<>();
1940
1941 try (DirectoryStream<Path> ds = java.nio.file.Files.newDirectoryStream(root.toPath())) {
1942 for (Path mnp : ds) {
1943 String mn = mnp.toFile().getName();
1944 if (mn.startsWith(FileConstants.META_PREFIX) ||
1945 TRASH.equals(mn)) {
1946 continue;
1947 }
1948
1949 try (DirectoryStream<Path> mds = java.nio.file.Files.newDirectoryStream(mnp)) {
1950 for (Path versionp : mds) {
1951 File v = versionp.toFile();
1952 if (!v.isDirectory()) {
1953 throw new IOException(versionp + ": Not a directory");
1954 }
1955 modules.add(jms.parseModuleId(mn, v.getName()));
1956 }
1957 }
1958 }
1959 }
1960 for (ModuleId mid : modules) {
1961 byte[] bs = Files.load(new File(moduleDir(root, mid), "info"));
1962 ModuleInfo mi = jms.parseModuleInfo(bs);
1963 addToDirectory(mi);
1964 }
1965 }
|