1 /* 2 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package org.openjdk.jigsaw.cli; 27 28 import java.lang.module.*; 29 import java.io.*; 30 import java.net.*; 31 import java.nio.file.Files; 32 import java.nio.file.Path; 33 import java.security.*; 34 import java.util.*; 35 36 import static java.lang.System.out; 37 import static java.lang.System.err; 38 39 import org.openjdk.jigsaw.*; 40 import org.openjdk.jigsaw.SimpleLibrary.StorageOption; 41 import org.openjdk.internal.joptsimple.*; 42 43 44 public class Librarian { 45 46 private static JigsawModuleSystem jms 47 = JigsawModuleSystem.instance(); 48 49 private static final File homeLibrary = Library.systemLibraryPath(); 50 51 static class Create extends Command<SimpleLibrary> { 52 protected void go(SimpleLibrary lib) 53 throws Command.Exception 54 { 55 noDry(); 56 finishArgs(); 57 File lp = libPath(opts); 58 File pp = null; 59 if (opts.has(parentPath)) { 60 pp = opts.valueOf(parentPath); 61 } else if (!opts.has("N")) { 62 pp = homeLibrary; 63 } 64 File natlibs = null; 65 if (opts.has(nativeLibs)) { 66 natlibs = opts.valueOf(nativeLibs); 67 } 68 File natcmds = null; 69 if (opts.has(nativeCmds)) { 70 natcmds = opts.valueOf(nativeCmds); 71 } 72 File configs = null; 73 if (opts.has(configFiles)) { 74 configs = opts.valueOf(configFiles); 75 } 76 77 Set<StorageOption> createOpts = new HashSet<>(); 78 if (opts.has("z")) 79 createOpts.add(StorageOption.DEFLATED); 80 81 try { 82 lib = SimpleLibrary.create(lp, pp, natlibs, natcmds, 83 configs, createOpts); 84 } catch (IOException x) { 85 throw new Command.Exception(x); 86 } 87 } 88 } 89 90 static class DumpClass extends Command<SimpleLibrary> { 91 protected void go(SimpleLibrary lib) 92 throws Command.Exception 93 { 94 noDry(); 95 String mids = takeArg(); 96 ModuleId mid = null; 97 try { 98 mid = jms.parseModuleId(mids); 99 } catch (IllegalArgumentException x) { 100 throw new Command.Exception(x.getMessage()); 101 } 102 String cn = takeArg(); 103 String ops = takeArg(); 104 finishArgs(); 105 byte[] bs = null; 106 try { 107 bs = lib.readClass(mid, cn); 108 if (bs == null) 109 throw new Command.Exception("%s: No such class in module %s", 110 cn, mid); 111 OutputStream fout = null; 112 try { 113 fout = (ops.equals("-") 114 ? System.out 115 : new FileOutputStream(ops)); 116 fout.write(bs); 117 } finally { 118 if (fout != null) 119 fout.close(); 120 } 121 } catch (IllegalArgumentException x) { 122 throw new Command.Exception(x.getMessage()); 123 } catch (IOException x) { 124 throw new Command.Exception(x); 125 } 126 } 127 } 128 129 static class Identify extends Command<SimpleLibrary> { 130 protected void go(SimpleLibrary lib) 131 throws Command.Exception 132 { 133 noDry(); 134 finishArgs(); 135 out.format("path %s%n", lib.root()); 136 out.format("version %d.%d%n", 137 lib.majorVersion(), lib.minorVersion()); 138 SimpleLibrary plib = lib.parent(); 139 while (plib != null) { 140 out.format("parent %s%n", plib.root()); 141 plib = plib.parent(); 142 } 143 } 144 } 145 146 static class Extract extends Command<SimpleLibrary> { 147 protected void go(SimpleLibrary lib) 148 throws Command.Exception 149 { 150 noDry(); 151 while (hasArg()) { 152 File module = new File(takeArg()); 153 File destination = null; 154 try (FileInputStream fis = new FileInputStream(module); 155 DataInputStream dis = new DataInputStream(fis); 156 ModuleFile.Reader reader = new ModuleFile.Reader(dis)) { 157 158 ModuleInfo mi = jms.parseModuleInfo(reader.readStart()); 159 destination = new File(mi.id().name()); 160 Path path = destination.toPath(); 161 Files.deleteIfExists(path); 162 Files.createDirectory(path); 163 reader.readRest(destination, false); 164 } 165 catch (IOException x) { 166 // Try to cleanup if an exception is thrown 167 if (destination != null && destination.exists()) 168 try { 169 FilePaths.deleteTree(destination.toPath()); 170 } 171 catch (IOException y) { 172 throw (Command.Exception) 173 new Command.Exception(y).initCause(x); 174 } 175 throw new Command.Exception(x); 176 } 177 } 178 finishArgs(); 179 } 180 } 181 182 static class Install extends Command<SimpleLibrary> { 183 protected void go(SimpleLibrary lib) 184 throws Command.Exception 185 { 186 String key = takeArg(); 187 File kf = new File(key); 188 boolean verifySignature = !opts.has("noverify"); 189 boolean strip = opts.has("G"); 190 191 // Old form: install <classes-dir> <module-name> ... 192 // 193 if (kf.exists() && kf.isDirectory()) { 194 noDry(); 195 List<Manifest> mfs = new ArrayList<>(); 196 while (hasArg()) 197 mfs.add(Manifest.create(takeArg(), kf)); 198 finishArgs(); 199 try { 200 lib.installFromManifests(mfs, strip); 201 } catch (ConfigurationException x) { 202 throw new Command.Exception(x); 203 } catch (IOException x) { 204 throw new Command.Exception(x); 205 } 206 return; 207 } 208 209 // Install one or more module file(s) 210 // 211 if (kf.exists() && kf.isFile()) { 212 noDry(); 213 List<File> fs = new ArrayList<>(); 214 fs.add(kf); 215 while (hasArg()) 216 fs.add(new File(takeArg())); 217 finishArgs(); 218 try { 219 lib.install(fs, verifySignature, strip); 220 } catch (ConfigurationException x) { 221 throw new Command.Exception(x); 222 } catch (IOException x) { 223 throw new Command.Exception(x); 224 } catch (SignatureException x) { 225 throw new Command.Exception(x); 226 } 227 return; 228 } 229 230 // Otherwise treat args as module-id queries 231 List<ModuleIdQuery> midqs = new ArrayList<>(); 232 String s = key; 233 for (;;) { 234 ModuleIdQuery mq = null; 235 try { 236 mq = jms.parseModuleIdQuery(s); 237 } catch (IllegalArgumentException x) { 238 throw new Command.Exception(x); 239 } 240 midqs.add(mq); 241 if (!hasArg()) 242 break; 243 s = takeArg(); 244 } 245 try { 246 boolean quiet = false; // ## Need -q 247 Resolution res = lib.resolve(midqs); 248 if (res.modulesNeeded().isEmpty()) { 249 if (!quiet) 250 out.format("Nothing to install%n"); 251 return; 252 } 253 if (!quiet) { 254 out.format("To install: %s%n", 255 res.modulesNeeded() 256 .toString().replaceAll("^\\[|\\]$", "")); 257 out.format("%d bytes to download%n", 258 res.downloadRequired()); 259 out.format("%d bytes to store%n", 260 res.spaceRequired()); 261 } 262 if (dry) 263 return; 264 lib.install(res, verifySignature, strip); 265 } catch (ConfigurationException x) { 266 throw new Command.Exception(x); 267 } catch (IOException x) { 268 throw new Command.Exception(x); 269 } catch (SignatureException x) { 270 throw new Command.Exception(x); 271 } 272 273 } 274 } 275 276 static class PreInstall extends Command<SimpleLibrary> { 277 protected void go(SimpleLibrary lib) 278 throws Command.Exception 279 { 280 noDry(); 281 File classes = new File(takeArg()); 282 File dst = new File(takeArg()); 283 List<Manifest> mfs = new ArrayList<Manifest>(); 284 while (hasArg()) 285 mfs.add(Manifest.create(takeArg(), classes)); 286 finishArgs(); 287 try { 288 lib.preInstall(mfs, dst); 289 } catch (IOException x) { 290 throw new Command.Exception(x); 291 } 292 } 293 } 294 295 static class DumpConfig extends Command<SimpleLibrary> { 296 protected void go(SimpleLibrary lib) 297 throws Command.Exception 298 { 299 noDry(); 300 String midqs = takeArg(); 301 ModuleIdQuery midq = null; 302 try { 303 midq = jms.parseModuleIdQuery(midqs); 304 } catch (IllegalArgumentException x) { 305 throw new Command.Exception(x.getMessage()); 306 } 307 finishArgs(); 308 try { 309 ModuleId mid = lib.findLatestModuleId(midq); 310 if (mid == null) 311 throw new Command.Exception(midq + ": No such module"); 312 Configuration<Context> cf = lib.readConfiguration(mid); 313 if (cf == null) 314 throw new Command.Exception(mid + ": Not a root module"); 315 cf.dump(out, verbose); 316 } catch (IOException x) { 317 throw new Command.Exception(x); 318 } 319 } 320 } 321 322 static class Config extends Command<SimpleLibrary> { 323 protected void go(SimpleLibrary lib) 324 throws Command.Exception 325 { 326 noDry(); 327 List<ModuleId> mids = new ArrayList<ModuleId>(); 328 try { 329 while (hasArg()) 330 mids.add(jms.parseModuleId(takeArg())); 331 } catch (IllegalArgumentException x) { 332 throw new Command.Exception(x.getMessage()); 333 } 334 try { 335 lib.configure(mids); 336 } catch (ConfigurationException x) { 337 throw new Command.Exception(x); 338 } catch (IOException x) { 339 throw new Command.Exception(x); 340 } 341 } 342 } 343 344 static class ReIndex extends Command<SimpleLibrary> { 345 protected void go(SimpleLibrary lib) 346 throws Command.Exception 347 { 348 noDry(); 349 List<ModuleId> mids = new ArrayList<ModuleId>(); 350 try { 351 while (hasArg()) 352 mids.add(jms.parseModuleId(takeArg())); 353 } catch (IllegalArgumentException x) { 354 throw new Command.Exception(x.getMessage()); 355 } 356 try { 357 lib.reIndex(mids); 358 } catch (ConfigurationException x) { 359 throw new Command.Exception(x); 360 } catch (IOException x) { 361 throw new Command.Exception(x); 362 } 363 } 364 } 365 366 static class Repos extends Command<SimpleLibrary> { 367 protected void go(SimpleLibrary lib) 368 throws Command.Exception 369 { 370 noDry(); 371 finishArgs(); 372 try { 373 RemoteRepositoryList rl = lib.repositoryList(); 374 int i = 0; 375 for (RemoteRepository rr : rl.repositories()) 376 out.format("%d %s%n", i++, rr.location()); 377 } catch (IOException x) { 378 throw new Command.Exception(x); 379 } 380 } 381 } 382 383 static URI parseURI(String s) 384 throws Command.Exception 385 { 386 try { 387 return new URI(s); 388 } catch (URISyntaxException x) { 389 throw new Command.Exception("URI syntax error: " 390 + x.getMessage()); 391 } 392 } 393 394 static class AddRepo extends Command<SimpleLibrary> { 395 protected void go(SimpleLibrary lib) 396 throws Command.Exception 397 { 398 noDry(); 399 URI u = parseURI(takeArg()); 400 int i = (opts.has(repoIndex) 401 ? opts.valueOf(repoIndex) 402 : Integer.MAX_VALUE); 403 finishArgs(); 404 try { 405 RemoteRepositoryList rl = lib.repositoryList(); 406 rl.add(u, i); 407 } catch (IOException x) { 408 throw new Command.Exception(x); 409 } 410 } 411 } 412 413 static class DelRepo extends Command<SimpleLibrary> { 414 protected void go(SimpleLibrary lib) 415 throws Command.Exception 416 { 417 noDry(); 418 URI u = null; 419 int i = -1; 420 if (hasArg()) { 421 String s = takeArg(); 422 if (!s.endsWith("/")) 423 s += "/"; 424 u = parseURI(s); 425 finishArgs(); 426 } 427 if (opts.has(repoIndex)) 428 i = opts.valueOf(repoIndex); 429 if (u != null && i != -1) { 430 throw new Command.Exception("del-repo: Cannot specify" 431 + " both -i and a URL"); 432 } 433 if (u == null && i == -1) { 434 throw new Command.Exception("del-repo: One of -i <index>" 435 + " or a URL required"); 436 } 437 try { 438 RemoteRepositoryList rl = lib.repositoryList(); 439 if (i != -1) { 440 rl.remove(rl.repositories().get(i)); 441 return; 442 } 443 for (RemoteRepository rr : rl.repositories()) { 444 if (rr.location().equals(u)) { 445 rl.remove(rr); 446 return; 447 } 448 } 449 throw new Command.Exception("No repository found for deletion"); 450 } catch (IOException x) { 451 throw new Command.Exception(x); 452 } 453 } 454 } 455 456 static class Refresh extends Command<SimpleLibrary> { 457 protected void go(SimpleLibrary lib) 458 throws Command.Exception 459 { 460 finishArgs(); 461 try { 462 RemoteRepositoryList rl = lib.repositoryList(); 463 int n = 0; 464 for (RemoteRepository rr : rl.repositories()) { 465 out.format("%s - ", rr.location()); 466 out.flush(); 467 boolean stale 468 = dry ? rr.isCatalogStale() : rr.updateCatalog(force); 469 if (stale) { 470 n++; 471 out.format(dry ? "out of date%n" : "updated%n"); 472 } else { 473 out.format("up to date%n"); 474 } 475 } 476 } catch (IOException x) { 477 throw new Command.Exception(x); 478 } 479 } 480 } 481 482 private static Map<String,Class<? extends Command<SimpleLibrary>>> commands 483 = new HashMap<>(); 484 485 static { 486 commands.put("add-repo", AddRepo.class); 487 commands.put("config", Config.class); 488 commands.put("create", Create.class); 489 commands.put("del-repo", DelRepo.class); 490 commands.put("dump-class", DumpClass.class); 491 commands.put("dump-config", DumpConfig.class); 492 commands.put("extract", Extract.class); 493 commands.put("id", Identify.class); 494 commands.put("identify", Identify.class); 495 commands.put("install", Install.class); 496 commands.put("list", Commands.ListLibrary.class); 497 commands.put("ls", Commands.ListLibrary.class); 498 commands.put("preinstall", PreInstall.class); 499 commands.put("refresh", Refresh.class); 500 commands.put("reindex", ReIndex.class); 501 commands.put("repos", Repos.class); 502 } 503 504 private OptionParser parser; 505 506 private static OptionSpec<Integer> repoIndex; // ## 507 private static OptionSpec<File> libPath; 508 private static OptionSpec<File> parentPath; 509 private static OptionSpec<File> nativeLibs; 510 private static OptionSpec<File> nativeCmds; 511 private static OptionSpec<File> configFiles; 512 513 private void usage() { 514 out.format("%n"); 515 out.format("usage: jmod add-repo [-i <index>] URL%n"); 516 out.format(" jmod extract <module-file> ...%n"); 517 out.format(" jmod config [<module-id> ...]%n"); 518 out.format(" jmod create [-L <library>] [-P <parent>]" + 519 " [--natlib <natlib>] [--natcmd <natcmd>] [--config <config>]%n"); 520 out.format(" jmod del-repo URL%n"); 521 out.format(" jmod dump-class <module-id> <class-name> <output-file>%n"); 522 out.format(" jmod dump-config <module-id>%n"); 523 out.format(" jmod identify%n"); 524 out.format(" jmod install [--noverify] [-n] <module-id-query> ...%n"); 525 out.format(" jmod install [--noverify] <module-file> ...%n"); 526 out.format(" jmod install <classes-dir> <module-name> ...%n"); 527 out.format(" jmod list [-v] [-p] [-R] [<module-id-query>]%n"); 528 out.format(" jmod preinstall <classes-dir> <dst-dir> <module-name> ...%n"); 529 out.format(" jmod refresh [-f] [-n] [-v]%n"); 530 out.format(" jmod reindex [<module-id> ...]%n"); 531 out.format(" jmod repos [-v]%n"); 532 out.format("%n"); 533 try { 534 parser.printHelpOn(out); 535 } catch (IOException x) { 536 throw new AssertionError(x); 537 } 538 out.format("%n"); 539 System.exit(0); 540 } 541 542 public static void run(String [] args) throws OptionException, Command.Exception { 543 new Librarian().exec(args); 544 } 545 546 private void exec(String[] args) throws OptionException, Command.Exception { 547 parser = new OptionParser(); 548 549 // ## Need subcommand-specific option parsing 550 libPath 551 = (parser.acceptsAll(Arrays.asList("L", "library"), 552 "Module-library location" 553 + " (default $JAVA_MODULES)") 554 .withRequiredArg() 555 .describedAs("path") 556 .ofType(File.class)); 557 parentPath 558 = (parser.acceptsAll(Arrays.asList("P", "parent-path"), 559 "Parent module-library location") 560 .withRequiredArg() 561 .describedAs("path") 562 .ofType(File.class)); 563 nativeLibs 564 = (parser.accepts("natlib", "Directory to store native libs") 565 .withRequiredArg() 566 .describedAs("dir") 567 .ofType(File.class)); 568 nativeCmds 569 = (parser.accepts("natcmd", "Directory to store native launchers") 570 .withRequiredArg() 571 .describedAs("dir") 572 .ofType(File.class)); 573 configFiles 574 = (parser.accepts("config", "Directory to store config files") 575 .withRequiredArg() 576 .describedAs("dir") 577 .ofType(File.class)); 578 parser.acceptsAll(Arrays.asList("N", "no-parent"), 579 "Use no parent library when creating"); 580 parser.acceptsAll(Arrays.asList("v", "verbose"), 581 "Enable verbose output"); 582 parser.acceptsAll(Arrays.asList("h", "?", "help"), 583 "Show this help message"); 584 parser.acceptsAll(Arrays.asList("p", "parent"), 585 "Apply operation to parent library, if any"); 586 parser.acceptsAll(Arrays.asList("z", "enable-compression"), 587 "Enable compression of module contents"); 588 repoIndex 589 = (parser.acceptsAll(Arrays.asList("i"), 590 "Repository-list index") 591 .withRequiredArg() 592 .describedAs("index") 593 .ofType(Integer.class)); 594 parser.acceptsAll(Arrays.asList("f", "force"), 595 "Force the requested operation"); 596 parser.acceptsAll(Arrays.asList("n", "dry-run"), 597 "Dry-run the requested operation"); 598 parser.acceptsAll(Arrays.asList("R", "repos"), 599 "List contents of associated repositories"); 600 parser.acceptsAll(Arrays.asList("noverify"), 601 "Do not verify module signatures. " 602 + "Treat as unsigned."); 603 parser.acceptsAll(Arrays.asList("G", "strip-debug"), 604 "Strip debug attributes during installation"); 605 606 if (args.length == 0) 607 usage(); 608 609 OptionSet opts = parser.parse(args); 610 if (opts.has("h")) 611 usage(); 612 List<String> words = opts.nonOptionArguments(); 613 if (words.isEmpty()) 614 usage(); 615 String verb = words.get(0); 616 Class<? extends Command<SimpleLibrary>> cmd = commands.get(verb); 617 if (cmd == null) 618 throw new Command.Exception("%s: unknown command", verb); 619 620 // Every command, except 'create' and 'extract', needs 621 // to have a valid reference to the library 622 SimpleLibrary lib = null; 623 if (!(verb.equals("create") || verb.equals("extract"))) { 624 File lp = libPath(opts); 625 try { 626 lib = SimpleLibrary.open(lp); 627 } catch (FileNotFoundException x) { 628 String msg = null; 629 File f = new File(x.getMessage()); 630 try { 631 f = f.getCanonicalFile(); 632 if (lp.getCanonicalFile().equals(f)) 633 msg = "No such library"; 634 else 635 msg = "Cannot open parent library " + f; 636 } catch (IOException y) { 637 throw new Command.Exception(y); 638 } 639 throw new Command.Exception("%s: %s", lp, msg); 640 } catch (IOException x) { 641 throw new Command.Exception(x); 642 } 643 } 644 try { 645 cmd.newInstance().run(lib, opts); 646 } catch (InstantiationException x) { 647 throw new AssertionError(x); 648 } catch (IllegalAccessException x) { 649 throw new AssertionError(x); 650 } 651 } 652 653 private static File libPath(OptionSet opts) { 654 if (opts.has(libPath)) { 655 return opts.valueOf(libPath); 656 } else { 657 String jm = System.getenv("JAVA_MODULES"); 658 if (jm != null) 659 return new File(jm); 660 else 661 return homeLibrary; 662 } 663 } 664 665 private Librarian() { } 666 667 public static void main(String[] args) { 668 try { 669 run(args); 670 } catch (OptionException x) { 671 err.println(x.getMessage()); 672 System.exit(1); 673 } catch (Command.Exception x) { 674 err.println(x.getMessage()); 675 x.printStackTrace(); 676 System.exit(1); 677 } 678 } 679 680 } --- EOF ---