src/share/classes/sun/module/tools/JRepo.java

Print this page




  28 import java.io.BufferedReader;
  29 import java.io.File;
  30 import java.io.FileNotFoundException;
  31 import java.io.IOException;
  32 import java.io.InputStreamReader;
  33 import java.io.OutputStream;
  34 import java.io.PrintStream;
  35 import java.io.PrintWriter;
  36 import java.io.StringWriter;
  37 import java.net.MalformedURLException;
  38 import java.net.URI;
  39 import java.net.URL;
  40 import java.net.URISyntaxException;
  41 import java.module.*;
  42 import java.module.annotation.ImportPolicyClass;
  43 import java.security.AccessController;
  44 import java.security.PrivilegedAction;
  45 import java.text.DateFormat;
  46 import java.util.Arrays;
  47 import java.util.ArrayList;
  48 import java.util.Collections;
  49 import java.util.Date;
  50 import java.util.HashMap;
  51 import java.util.HashSet;
  52 import java.util.LinkedHashMap;
  53 import java.util.List;
  54 import java.util.Map;
  55 import java.util.Set;

  56 import sun.module.repository.RepositoryConfig;
  57 import sun.security.action.GetPropertyAction;
  58 import sun.tools.jar.CommandLine;
  59 
  60 /**
  61  * Java Modules Repository Management Tool
  62  * @since 1.7
  63  */
  64 public class JRepo {
  65     public static final boolean DEBUG;
  66 
  67     static {
  68         DEBUG = (AccessController.doPrivileged(new GetPropertyAction("sun.module.tools.debug")) != null);
  69     }
  70 
  71     private static void debug(String s) {
  72         System.err.println(s);
  73     }
  74 
  75     /** For printing user output. */


  97     private static final String MDFormatVerbose = " %s";
  98 
  99     /** String containing column headings for name & version. */
 100     private static final String maiHeading;
 101 
 102     /** String containing column headings for additional/verbose module information. */
 103     private static final String maiHeadingVerbose;
 104 
 105     /** Indicates that parent repositories should be used by a command. */
 106     private static final Flag parentFlag = new Flag('p');
 107 
 108     /** Indicates that command output should be verbose. */
 109     private static final Flag verboseFlag = new Flag('v');
 110 
 111     /** Location of repository; if not given uses application repository. */
 112     private static final RepositoryFlag repositoryFlag = new RepositoryFlag();
 113 
 114     /** Indicates dependencies command should display info on core modules. */
 115     private static final Flag javaseFlag = new Flag('j');
 116 
 117     /** Provides way to specify platform binding fo dependencies command. */
 118     private static final BindingFlag bindingFlag = new BindingFlag();
 119 
 120     /** Contains the flags that are common to all commands. */
 121     private static final HashMap<Character, Flag> commonFlags = new HashMap<Character, Flag>();
 122 
 123     static {
 124         repositoryFlag.register(commonFlags);
 125         verboseFlag.register(commonFlags);

 126 
 127         StringWriter sw = new StringWriter();
 128         PrintWriter pw = new PrintWriter(sw);
 129         pw.printf(MAIFormat, "Name", "Version"); // XXX i18n
 130         maiHeading = sw.toString();
 131 
 132         sw = new StringWriter();
 133         pw = new PrintWriter(sw);
 134         pw.printf(MAIFormat, "Name", "Version"); // XXX i18n
 135         pw.printf(MAIFormatVerbose, "Platform", "Arch", "Modified", "Filename"); // XXX i18n
 136         maiHeadingVerbose = sw.toString();
 137     }
 138 
 139     public JRepo(OutputStream out, OutputStream err, BufferedReader reader) {
 140         this(new PrintStream(out), new PrintStream(err), reader);
 141     }
 142 
 143     public JRepo(PrintStream out, PrintStream err, BufferedReader reader) {
 144         msg = new Messenger("jrepo", out, err, reader);
 145         synchronized(commands) {
 146             if (commands.isEmpty()) {
 147                 new ListCommand().register(commands);
 148                 new InstallCommand().register(commands);


 149                 new UninstallCommand().register(commands);
 150                 new DependenciesCommand().register(commands);
 151             }
 152         }
 153         reset();
 154     }
 155 
 156     public static void main(String[] args) {
 157         JRepo jrepo = new JRepo(
 158             System.out, System.err,
 159             new BufferedReader(new InputStreamReader(System.in)));
 160         System.exit(jrepo.run(args) ? 0 : 1);
 161     }
 162 
 163     public synchronized boolean run(String[] args) {
 164         reset();
 165 
 166         if (DEBUG) { for (String s : args) debug("arg: '" + s + "'"); }
 167         Command cmd = parseArgs(args);
 168         if (DEBUG && cmd != null) debug("running " + cmd);
 169         if (cmd == null) {
 170             return false;
 171         }
 172 





 173         Repository repo = null;
 174         try {
 175              repo = getRepository();
 176              repo.shutdownOnExit(true);
 177         } catch (IOException ex) {
 178             msg.fatalError(ex.getMessage());
 179             return false;
 180         }
 181 
 182         boolean rc = cmd.run(repo, msg);
 183         if (DEBUG) debug(cmd + " returned " + rc);
 184         return rc;
 185 
 186     }
 187 
 188     /**
 189      * Gets the repository based on command line flags.
 190      * @return Reposistory based on the value from {@code repositoryFlag} if
 191      * that is non-null, else the application repository.
 192      */


 208                 if (f.exists() && f.canRead()) {
 209                     rc = Modules.newLocalRepository(
 210                         "jrepo",
 211                         f.getCanonicalFile(), null,
 212                         RepositoryConfig.getApplicationRepository());
 213                 } else {
 214                     throw new IOException("Cannot access repository at " // XXX i18n
 215                                           + repositoryLocation);
 216                 }
 217             }
 218         }
 219         return rc;
 220     }
 221 
 222     /** Reset instance state to default values. */
 223     private void reset() {
 224         moduleName = null;
 225         parentFlag.reset();
 226         repositoryFlag.reset();
 227         verboseFlag.reset();

 228         for (Command cmd : commands.values()) {
 229             cmd.reset();
 230         }
 231     }
 232 
 233 
 234     /** Parse command line arguments.*/
 235     private Command parseArgs(String[] args) {
 236         /* Preprocess and expand @file arguments */
 237         try {
 238             args = CommandLine.parse(args);
 239         } catch (FileNotFoundException e) {
 240             msg.fatalError(msg.formatMsg("error.cant.open", e.getMessage()));
 241             return null;
 242         } catch (IOException e) {
 243             msg.fatalError(msg.formatMsg("caught.exception", e.getMessage()));
 244             return null;
 245         }
 246 
 247         if (args.length < 1) {
 248             usageError();
 249             return null;
 250         }
 251 
 252         Command cmd = null;
 253         for (Map.Entry<String, Command> entry : commands.entrySet()) {
 254             if (entry.getKey().startsWith(args[0])) {
 255                 cmd = entry.getValue();
 256                 break;
 257             }
 258         }
 259         if (DEBUG) debug("found command " + cmd);
 260         if (cmd == null) {
 261             usageError();
 262             return null;
 263         }
 264 
 265         try {
 266             int numFlags = cmd.parseFlags(args, commonFlags);




 267             args = Arrays.copyOfRange(args, numFlags + 1, args.length);
 268             try {
 269                 if (cmd.parseArgs(args, msg)) {
 270                     return cmd;
 271                 } else {
 272                     usageError();
 273                     return null;
 274                 }
 275             } catch (ArrayIndexOutOfBoundsException e) {
 276                 usageError();
 277             }
 278         } catch (IllegalArgumentException ex) {
 279             msg.error(ex.getMessage());
 280             usageError();
 281             return null;
 282         }
 283         return cmd;
 284     }
 285 
 286 
 287     /** Returns a user-grokkable description of the repository. */
 288     private static String getRepositoryText(Repository repo) {
 289         String rc;
 290 /*        URI u = repo.getSourceLocation();
 291         if (u == null) {
 292             rc = "Bootstrap repository";
 293         } else {
 294             try {
 295                 rc = "Repository " + u.toURL().toExternalForm();
 296             } catch (MalformedURLException ex) {
 297                 rc = "Repository unknown";
 298             }
 299         }
 300  */
 301         rc = "[" + repo.toString() + "]";
 302 
 303         return rc;
 304     }
 305 
 306     private static String getMAIText(ModuleArchiveInfo mai) {
 307         StringWriter sw = new StringWriter();
 308         PrintWriter pw = new PrintWriter(sw);
 309         pw.printf(MAIFormat, mai.getName(), mai.getVersion());
 310         if (verboseFlag.isEnabled()) {
 311             long t = mai.getLastModified();
 312             String lastMod = null;
 313             if (t != 0) {
 314                 lastMod = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date(t));
 315             }
 316             pw.printf(MAIFormatVerbose,
 317                 mai.getPlatform() == null ? "generic" : mai.getPlatform(),
 318                 mai.getArch() == null ? "generic" : mai.getArch(),
 319                 lastMod == null ? "n/a" : lastMod,
 320                 mai.getFileName() == null ? "n/a" : mai.getFileName()
 321                 );
 322         }
 323         return sw.toString();
 324     }
 325 
 326     private static String getMText(Module m) {
 327         return getMDText(m.getModuleDefinition());
 328     }
 329 
 330     private static String getMDText(ModuleDefinition md) {
 331         StringWriter sw = new StringWriter();
 332         PrintWriter pw = new PrintWriter(sw);
 333         pw.printf(MDFormat, md.getName(), md.getVersion());
 334         if (verboseFlag.isEnabled()) {
 335             pw.printf(MDFormatVerbose,
 336 //                      md.getRepository() == Repository.getBootstrapRepository()
 337 //                      ? "bootstrap"
 338 //                      : md.getRepository().getSourceLocation().toString());
 339                      md.getRepository().getName());
 340         }
 341         return sw.toString();
 342     }
 343 
 344     void usageError() {
 345         usageError(msg);
 346     }
 347 
 348     static void usageError(Messenger msg) {
 349         if (usage == null) {
 350             StringBuilder ub = new StringBuilder(
 351                 "Usage: jrepo <command>\nwhere <command> includes:"); // XXX i18n
 352             for (Command c : commands.values()) {
 353                 String u = c.usage();
 354                 if (u != null) {
 355                     ub.append("\n    ").append(c.usage());
 356                 }
 357             }
 358             usage = ub.toString();


 415         }
 416 
 417         @Override
 418         int set(String[] args, int pos) throws IllegalArgumentException {
 419             int rc = super.set(args, pos);
 420             location = args[pos + 1];
 421             return rc + 1;
 422         }
 423 
 424         String getLocation() {
 425             return location;
 426         }
 427 
 428         @Override
 429         void reset() {
 430             super.reset();
 431             location = null;
 432         }
 433     }
 434 
 435     private static class BindingFlag extends Flag {
 436         String platform;
 437         String arch;
 438 
 439         BindingFlag() {
 440             super('b');
 441         }
 442 
 443         @Override
 444         int set(String[] args, int pos) throws IllegalArgumentException {
 445             int rc = super.set(args, pos);
 446             String[] binding = args[pos + 1].split("-");
 447             if (binding.length != 2) {
 448                 throw new IllegalArgumentException(
 449                     "Must 2 and only 2 elements for platform-arch");
 450             }
 451             platform = binding[0];
 452             arch = binding[1];
 453             return rc + 1;
 454         }
 455 
 456         String getPlatform() {
 457             return platform;
 458         }
 459 
 460         String getArch() {
 461             return arch;
 462         }
 463 
 464         @Override
 465         void reset() {
 466             super.reset();
 467             platform = null;
 468             arch = null;
 469         }
 470     }
 471 
 472     /*
 473      * Command types: An abstract base class, plus one concrete class for
 474      * each Command.
 475      */
 476 
 477     /*
 478      * Represents a Command.
 479      */
 480     private static abstract class Command {
 481         private final String name;
 482 
 483         Command(String name) {
 484             this.name = name;
 485         }
 486 
 487         /** Adds this command to the given registry. */
 488         void register(Map<String, Command> registry) {
 489             registry.put(name, this);
 490         }
 491 
 492         void reset() {
 493             // Empty; subclasses can implement
 494         }
 495 
 496         // Flags differ from arguments in that they have the form "-X [opt]".
 497         // Flags appear in a command line before arguments.
 498 
 499         /** Parses the arguments particular to this command. */
 500         abstract boolean parseArgs(String[] args, Messenger msg);
 501 
 502         /**
 503          * Parse the Flags for this command.
 504          * @return number of flags found.
 505          * @throws IllegalArgumentException if an invalid flag is given.
 506          */
 507         int parseFlags(String[] args, Map<Character, Flag> flags) {
 508             int rc = 0;
 509             int i = 0;
 510             while (i < args.length) {
 511                 String s = args[i];
 512                 if (s.length() == 2 && s.charAt(0) == '-') {
 513                     Flag f = flags.get(s.charAt(1));
 514                     if (f != null) {
 515                         int numConsumed = f.set(args, i);
 516                         i += numConsumed;  // Increases at each iteration.
 517                         rc += numConsumed; // Increases only when the arg is a flag.
 518                     } else {
 519                         throw new IllegalArgumentException("unrecognized flag: " // XXX i18n
 520                                                            + args[i]);
 521                     }



 522                 } else {
 523                     i++;
 524                 }
 525             }
 526             return rc;
 527         }



























 528 
 529         /** Represents the actual behavior of the command. */
 530         abstract boolean run(Repository repo, Messenger msg);
 531 
 532         /** Returns a usage string describing this command, or null if the






 533          * command is a synonym for another command.
 534          */
 535         abstract String usage();
 536 
 537         @Override
 538         public String toString() { return name; }
 539 
 540         /**
 541          * RepositoryVisitor types walk a parent chain of repositories, invoking
 542          * {@code doit} in each one.  The abstract base class provides the recursion;
 543          * each concrete subclass provides the per-repository behavior.  The
 544          * recursion is such that the bootstrap repository is visited first,
 545          * and the application repository is last.
 546          */
 547         abstract class RepositoryVisitor {
 548             abstract void doit(Repository repo, Messenger msg);
 549 
 550             void preVisit(Messenger msg) { }
 551 
 552             void postVisit(Messenger msg) { }


 565                 postVisit(msg);
 566             }
 567         }
 568     }
 569 
 570 
 571     /** Lists dependencies of a module. */
 572     private class DependenciesCommand extends Command {
 573         @SuppressWarnings("unchecked")
 574         private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
 575 
 576         private String name;
 577         private Version version;
 578 
 579         // For printing module name, version.
 580         private String moduleString;
 581 
 582         DependenciesCommand() {
 583             super("dependencies");
 584             javaseFlag.register(myFlags);
 585             bindingFlag.register(myFlags);
 586         }
 587 
 588         @Override
 589         void reset() {
 590             javaseFlag.reset();
 591             bindingFlag.reset();
 592         }
 593 
 594         @Override
 595         int parseFlags(String[] args, Map<Character, Flag> flags) {
 596             return super.parseFlags(args, myFlags);
 597         }
 598 
 599         boolean parseArgs(String[] args, Messenger msg) {
 600             if (args.length > 0) {
 601                 name = args[0];
 602             }
 603             if (args.length > 1) {
 604                 try {
 605                     version = Version.valueOf(args[1]);
 606                 } catch (IllegalArgumentException ex) {
 607                     return false;
 608                 }
 609             }
 610             if (args.length > 2) {
 611                 return false;
 612             }
 613 
 614             if (DEBUG) debug("name: " + name + " ver: " + version
 615                              + " plat: " + bindingFlag.getPlatform()
 616                              + " arch: " + bindingFlag.getArch());
 617             return true;
 618         }
 619 
 620         boolean run(Repository repo, Messenger msg) {
 621             boolean rc = true;
 622             if (name != null) {
 623                 VersionConstraint vc = (version == null
 624                                         ? VersionConstraint.DEFAULT
 625                                         : version.toVersionConstraint());
 626                 printHeader(name, vc);
 627                 rc = depend(repo, name, vc,
 628                             bindingFlag.getPlatform(), bindingFlag.getArch());
 629                 printTrailer(null);
 630             } else {
 631                 for (ModuleArchiveInfo mai : repo.list()) {
 632                     reset();
 633                     String maiName = mai.getName();
 634                     VersionConstraint vc = mai.getVersion().toVersionConstraint();
 635                     printHeader(maiName, vc);
 636                     rc &= depend(repo, maiName, vc,
 637                                  mai.getPlatform(), mai.getArch());
 638                     printTrailer("\n");
 639                 }
 640             }
 641             return rc;
 642         }
 643 
 644         boolean depend(Repository repo, String name, VersionConstraint constraint,
 645                        String platform, String arch) {
 646             ImportTraverser traverser = new ImportTraverser();
 647             ImportTraverser.Visitor visitor = new ImportVisitor(traverser, msg);
 648             try {
 649                 traverser.traverse(visitor, repo, name, constraint, platform, arch);
 650                 if (traverser.traversedAny()) {
 651                     return true;
 652                 }
 653                 if (verboseFlag.isEnabled()) {
 654                     msg.error("Cannot find module " + name + " in " // XXX i18n
 655                               + getRepositoryText(repo));
 656                 }
 657                 return false;
 658             } catch (ModuleInitializationException ex) {
 659                 msg.error("Cannot instantiate module for " + name // XXX i18n
 660                           + ": " + ex);
 661                 return false;
 662             }
 663         }
 664 
 665 
 666         void printHeader(String name, VersionConstraint vc) {
 667             if (verboseFlag.isEnabled()) {
 668                 msg.println("Dependencies for " // XXXi18n
 669                             + name + "-" + vc + ":");
 670             }
 671         }
 672 
 673         void printTrailer(String s) {
 674             if (verboseFlag.isEnabled()) {
 675                 msg.print(s);
 676             }
 677         }
 678 
 679         String usage() {
 680             return "dependencies [-v] [-r repositoryLocation] [-b platform-arch]"// XXX i18n
 681                 + " [moduleName [moduleVersion] ]\n"
 682                 + "        Lists all modules on which identified modules depend.\n"
 683                 + "        If no moduleName is given, lists dependencies of all"
 684                 + " module archives in the repository\n"
 685                 + "        which are instantiable on the current platform.";
 686         }
 687     }
 688 
 689 
 690     private static class ImportVisitor extends ImportTraverser.Visitor {
 691         private final Messenger msg;
 692 
 693         private static final String INDENT = "    ";
 694         private static final int INDENT_LENGTH = INDENT.length();
 695 
 696         private String indent = "";
 697 
 698         ImportVisitor(ImportTraverser traverser, Messenger msg) {
 699             super(traverser);
 700             this.msg = msg;
 701         }


 719         @Override
 720         protected void visit(Module m) {
 721             printModule(m);
 722         }
 723 
 724         @Override
 725         protected void postVisit(Module m) {
 726             if (javaseFlag.isEnabled() == false
 727                 && m.getModuleDefinition().getName().startsWith("java.se")) {
 728                 // empty
 729             } else {
 730                 indent = indent.substring(INDENT_LENGTH);
 731             }
 732         }
 733 
 734         void printModule(Module m) {
 735             msg.println(indent + getMText(m));
 736         }
 737     }
 738 























 739     /** Installs a JAM into a repository. */
 740     private class InstallCommand extends Command {






 741         private String jamName;
 742 
 743         InstallCommand() {
 744             super("install");

 745         }
 746 











 747         boolean parseArgs(String[] args, Messenger msg) {
 748             boolean rc = false;
 749             if (!parentFlag.isEnabled()
 750                     && repositoryFlag.getLocation() != null
 751                     && args.length == 1) {
 752                 jamName = args[0];
 753                 rc = true;
 754             }
 755             return rc;
 756         }
 757 
 758         boolean run(Repository repo, Messenger msg) {
 759             if (repo != null) {
 760                 String jamURL = null;
 761                 File f = new File(jamName);
 762                 if (f.canRead()) {
 763                     try {
 764                         String path = f.getCanonicalPath();
 765                         // Ensure that path starts with a "/" (it does not on
 766                         // some systems, e.g. Windows).
 767                         if (!path.startsWith("/")) {
 768                             path = "/" + path;
 769                         }
 770                         jamURL = "file://" + path;
 771                     } catch (IOException ex) {
 772                         msg.error("Cannot install " + jamName + ": " + ex.getMessage());
 773                         return false;
 774                     }
 775                 } else {
 776                     jamURL = jamName;
 777                 }
 778                 try {
 779                     ModuleArchiveInfo mai = repo.install(new URL(jamURL).toURI());
 780                     if (verboseFlag.isEnabled()) {
 781                         msg.println("Installed " + jamName + ": " + getMAIText(mai));
 782                     }


















 783                     return true;


 784                 } catch (URISyntaxException ex) {
 785                     msg.error("Cannot install " + jamName + ": no such file, or malformed URI");
 786                 } catch (MalformedURLException ex) {
 787                     msg.error("Cannot install " + jamName + ": no such file, or malformed URL");
 788                 } catch (IOException ex) {
 789                     msg.error("Cannot install " + jamName + ": " + ex.getMessage());
 790                 }
 791             }
 792             return false;
 793         }
 794 
 795         String usage() {
 796             return "install [-v] -r repositoryLocation jamFile | jamURL\n" // XXX i18n
 797                 +  "        installs a module into a repository";
 798         }
 799     }
 800 
 801     /** Uninstalls a module from a repository. */
 802     private class UninstallCommand extends Command {
 803         @SuppressWarnings("unchecked")
 804         private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
 805 
 806         private Flag forceFlag = new Flag('f');
 807 
 808         private Flag interactiveFlag = new Flag('i');
 809 
 810         private String moduleName;
 811 
 812         private Version version;
 813 
 814         private String platformBinding;
 815 
 816         UninstallCommand() {
 817             super("uninstall");


 899                 } else if (forceFlag.isEnabled()) {
 900                     if (DEBUG) debug("forced uninstall of multiple matches");
 901                     rc = true;
 902                     for (ModuleArchiveInfo mai : found) {
 903                         if (!uninstall(repo, mai, msg)) {
 904                             if (DEBUG) debug("uninstall failed for " + getMAIText(mai) + " in " + repo.toString());
 905                             rc = false;
 906                             break;
 907                         }
 908                     }
 909                 } else if (interactiveFlag.isEnabled()) {
 910                     rc = uninstallInteractive(repo, found, msg);
 911                 }
 912             }
 913             return rc;
 914         }
 915 
 916         String usage() {
 917             return "uninstall [-v] [-f | -i] -r repositoryLocation moduleName"// XXX i18n
 918                 + " [moduleVersion] [modulePlatformBinding]\n"
 919                 +  "        removes a module from a repository, along with associated files.";
 920         }
 921 
 922         /** Uninstall the ModuleArchiveInfo from the Repository. */
 923         private boolean uninstall(Repository repo, ModuleArchiveInfo mai, Messenger msg) {
 924             boolean rc = false;
 925             if (DEBUG) debug("Uninstalling " + getMAIText(mai));
 926             try {
 927                 rc = repo.uninstall(mai);
 928                 if (verboseFlag.isEnabled()) {
 929                     if (rc) {
 930                         msg.println("Uninstalled " + getMAIText(mai)); // XXX i18n
 931                     } else {
 932                         msg.error("Failed to uninstall " + getMAIText(mai)); // XXX i18n
 933                     }
 934                 }
 935             } catch (Exception ex) {
 936                 msg.error("Exception while uninstalling " + getInfo()
 937                           + ": " + ex.getMessage());
 938             }
 939             return rc;


1010                             rc = true;
1011                         }
1012                     }
1013                 }
1014             }
1015             return rc;
1016         }
1017 
1018         private String getInfo() {
1019             String s = moduleName;
1020             if (version != null) {
1021                 s += " with version " + version;
1022             }
1023             if (platformBinding != null) {
1024                 s += " and platform-binding of " + platformBinding;
1025             }
1026             return s;
1027         }
1028     }
1029 






















































1030     /** Prints information about modules found in repositories. */
1031     private class ListCommand extends Command {
1032         @SuppressWarnings("unchecked")
1033         private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
1034 
1035         ListCommand() {
1036             super("list");
1037             parentFlag.register(myFlags);
1038         }
1039 
1040         @Override
1041         void reset() {
1042             parentFlag.reset();
1043         }
1044 
1045         @Override
1046         int parseFlags(String[] args, Map<Character, Flag> flags) {
1047             return super.parseFlags(args, myFlags);
1048         }
1049 


1058             }
1059             return rc;
1060         }
1061 
1062         boolean run(Repository repo, Messenger msg) {
1063             ListRepositoryVisitor visitor = new ListRepositoryVisitor();
1064             visitor.run(repo, msg);
1065             boolean found = visitor.wasFound();
1066             if (verboseFlag.isEnabled() && !found) {
1067                 if (moduleName != null) {
1068                     msg.error("Could not find module name starting with '"// XXX i18n
1069                               + moduleName + "'");
1070                 } else {
1071                     msg.error("Could not find any modules"); // XXX i18n
1072                 }
1073             }
1074             return found;
1075         }
1076 
1077         String usage() {
1078             return "list [-v] [-p] [-r repositoryLocation] moduleName\n"// XXX i18n
1079                 +  "        lists the modules in the repository";

1080         }
1081 
1082         class ListRepositoryVisitor extends RepositoryVisitor {
1083             private boolean found = false;
1084 
1085             boolean wasFound() { return found; }
1086 
1087             void doit(Repository repo, Messenger msg) {
1088                 boolean printedHeader = false;
1089                 List<ModuleArchiveInfo> maiList = repo.list();
1090                 if (maiList.size() == 0 && verboseFlag.isEnabled()) {
1091                     msg.println(getRepositoryText(repo));
1092                     msg.println("   empty");
1093                 } else {
1094                     for (ModuleArchiveInfo mai : repo.list()) {



1095                         if (moduleName == null || mai.getName().startsWith(moduleName)) {
1096                             if (!printedHeader) {
1097                                 msg.println(getRepositoryText(repo));
1098                                 msg.println(verboseFlag.isEnabled() ? maiHeadingVerbose : maiHeading);
1099                                 printedHeader = true;
1100                             }
1101                             msg.println(getMAIText(mai));
1102                             found = true;
1103                         }
1104                     }
1105                 }
1106             }
1107         }
1108     }















































































































1109 }


  28 import java.io.BufferedReader;
  29 import java.io.File;
  30 import java.io.FileNotFoundException;
  31 import java.io.IOException;
  32 import java.io.InputStreamReader;
  33 import java.io.OutputStream;
  34 import java.io.PrintStream;
  35 import java.io.PrintWriter;
  36 import java.io.StringWriter;
  37 import java.net.MalformedURLException;
  38 import java.net.URI;
  39 import java.net.URL;
  40 import java.net.URISyntaxException;
  41 import java.module.*;
  42 import java.module.annotation.ImportPolicyClass;
  43 import java.security.AccessController;
  44 import java.security.PrivilegedAction;
  45 import java.text.DateFormat;
  46 import java.util.Arrays;
  47 import java.util.ArrayList;
  48 import java.util.Comparator;
  49 import java.util.Date;
  50 import java.util.HashMap;
  51 import java.util.HashSet;
  52 import java.util.LinkedHashMap;
  53 import java.util.List;
  54 import java.util.Map;
  55 import java.util.Set;
  56 import java.util.TreeSet;
  57 import sun.module.repository.RepositoryConfig;
  58 import sun.security.action.GetPropertyAction;
  59 import sun.tools.jar.CommandLine;
  60 
  61 /**
  62  * Java Modules Repository Management Tool
  63  * @since 1.7
  64  */
  65 public class JRepo {
  66     public static final boolean DEBUG;
  67 
  68     static {
  69         DEBUG = (AccessController.doPrivileged(new GetPropertyAction("sun.module.tools.debug")) != null);
  70     }
  71 
  72     private static void debug(String s) {
  73         System.err.println(s);
  74     }
  75 
  76     /** For printing user output. */


  98     private static final String MDFormatVerbose = " %s";
  99 
 100     /** String containing column headings for name & version. */
 101     private static final String maiHeading;
 102 
 103     /** String containing column headings for additional/verbose module information. */
 104     private static final String maiHeadingVerbose;
 105 
 106     /** Indicates that parent repositories should be used by a command. */
 107     private static final Flag parentFlag = new Flag('p');
 108 
 109     /** Indicates that command output should be verbose. */
 110     private static final Flag verboseFlag = new Flag('v');
 111 
 112     /** Location of repository; if not given uses application repository. */
 113     private static final RepositoryFlag repositoryFlag = new RepositoryFlag();
 114 
 115     /** Indicates dependencies command should display info on core modules. */
 116     private static final Flag javaseFlag = new Flag('j');
 117 
 118     /** Causes an individual command to print help and exit. */
 119     private static final Flag helpFlag = new Flag('h');
 120 
 121     /** Contains the flags that are common to all commands. */
 122     private static final HashMap<Character, Flag> commonFlags = new HashMap<Character, Flag>();
 123 
 124     static {
 125         repositoryFlag.register(commonFlags);
 126         verboseFlag.register(commonFlags);
 127         helpFlag.register(commonFlags);
 128 
 129         StringWriter sw = new StringWriter();
 130         PrintWriter pw = new PrintWriter(sw);
 131         pw.printf(MAIFormat, "Name", "Version"); // XXX i18n
 132         maiHeading = sw.toString();
 133 
 134         sw = new StringWriter();
 135         pw = new PrintWriter(sw);
 136         pw.printf(MAIFormat, "Name", "Version"); // XXX i18n
 137         pw.printf(MAIFormatVerbose, "Platform", "Arch", "Modified", "Filename"); // XXX i18n
 138         maiHeadingVerbose = sw.toString();
 139     }
 140 
 141     public JRepo(OutputStream out, OutputStream err, BufferedReader reader) {
 142         this(new PrintStream(out), new PrintStream(err), reader);
 143     }
 144 
 145     public JRepo(PrintStream out, PrintStream err, BufferedReader reader) {
 146         msg = new Messenger("jrepo", out, err, reader);
 147         synchronized(commands) {
 148             if (commands.isEmpty()) {
 149                 new DependenciesCommand().register(commands);
 150                 new InstallCommand().register(commands);
 151                 new HelpCommand().register(commands);
 152                 new ListCommand().register(commands);
 153                 new UninstallCommand().register(commands);
 154                 new ValidateCommand().register(commands);
 155             }
 156         }
 157         reset();
 158     }
 159 
 160     public static void main(String[] args) {
 161         JRepo jrepo = new JRepo(
 162             System.out, System.err,
 163             new BufferedReader(new InputStreamReader(System.in)));
 164         System.exit(jrepo.run(args) ? 0 : 1);
 165     }
 166 
 167     public synchronized boolean run(String[] args) {
 168         reset();
 169 
 170         if (DEBUG) { for (String s : args) debug("arg: '" + s + "'"); }
 171         Command cmd = parseArgs(args);
 172         if (DEBUG && cmd != null) debug("running " + cmd);
 173         if (cmd == null) {
 174             return false;
 175         }
 176 
 177         if (helpFlag.isEnabled()) {
 178             cmd.usageError(msg);
 179             return true;
 180         }
 181 
 182         Repository repo = null;
 183         try {
 184              repo = getRepository();
 185              repo.shutdownOnExit(true);
 186         } catch (IOException ex) {
 187             msg.fatalError(ex.getMessage());
 188             return false;
 189         }
 190 
 191         boolean rc = cmd.run(repo, msg);
 192         if (DEBUG) debug(cmd + " returned " + rc);
 193         return rc;
 194 
 195     }
 196 
 197     /**
 198      * Gets the repository based on command line flags.
 199      * @return Reposistory based on the value from {@code repositoryFlag} if
 200      * that is non-null, else the application repository.
 201      */


 217                 if (f.exists() && f.canRead()) {
 218                     rc = Modules.newLocalRepository(
 219                         "jrepo",
 220                         f.getCanonicalFile(), null,
 221                         RepositoryConfig.getApplicationRepository());
 222                 } else {
 223                     throw new IOException("Cannot access repository at " // XXX i18n
 224                                           + repositoryLocation);
 225                 }
 226             }
 227         }
 228         return rc;
 229     }
 230 
 231     /** Reset instance state to default values. */
 232     private void reset() {
 233         moduleName = null;
 234         parentFlag.reset();
 235         repositoryFlag.reset();
 236         verboseFlag.reset();
 237         helpFlag.reset();
 238         for (Command cmd : commands.values()) {
 239             cmd.reset();
 240         }
 241     }
 242 
 243 
 244     /** Parse command line arguments.*/
 245     private Command parseArgs(String[] args) {
 246         /* Preprocess and expand @file arguments */
 247         try {
 248             args = CommandLine.parse(args);
 249         } catch (FileNotFoundException e) {
 250             msg.fatalError(msg.formatMsg("error.cant.open", e.getMessage()));
 251             return null;
 252         } catch (IOException e) {
 253             msg.fatalError(msg.formatMsg("caught.exception", e.getMessage()));
 254             return null;
 255         }
 256 
 257         if (args.length < 1) {
 258             usageError();
 259             return null;
 260         }
 261 
 262         Command cmd = null;
 263         for (Map.Entry<String, Command> entry : commands.entrySet()) {
 264             if (entry.getKey().startsWith(args[0])) {
 265                 cmd = entry.getValue();
 266                 break;
 267             }
 268         }
 269         if (DEBUG) debug("found command " + cmd);
 270         if (cmd == null) {
 271             usageError();
 272             return null;
 273         }
 274 
 275         try {
 276             int numFlags = cmd.parseFlags(args, commonFlags);
 277             if (helpFlag.isEnabled()) {
 278                 return cmd;
 279             }
 280             
 281             args = Arrays.copyOfRange(args, numFlags + 1, args.length);
 282             try {
 283                 if (cmd.parseArgs(args, msg)) {
 284                     return cmd;
 285                 } else {
 286                     cmd.usageError(msg);
 287                     return null;
 288                 }
 289             } catch (ArrayIndexOutOfBoundsException e) {
 290                 usageError();
 291             }
 292         } catch (IllegalArgumentException ex) {
 293             msg.error(ex.getMessage());
 294             cmd.usageError(msg);
 295             return null;
 296         }
 297         return cmd;
 298     }
 299 
 300 
 301     /** Returns a user-grokkable description of the repository. */
 302     private static String getRepositoryText(Repository repo) {
 303         String rc;











 304         rc = "[" + repo.toString() + "]";
 305 
 306         return rc;
 307     }
 308 
 309     private static String getMAIText(ModuleArchiveInfo mai) {
 310         StringWriter sw = new StringWriter();
 311         PrintWriter pw = new PrintWriter(sw);
 312         pw.printf(MAIFormat, mai.getName(), mai.getVersion());
 313         if (verboseFlag.isEnabled()) {
 314             long t = mai.getLastModified();
 315             String lastMod = null;
 316             if (t != 0) {
 317                 lastMod = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date(t));
 318             }
 319             pw.printf(MAIFormatVerbose,
 320                 mai.getPlatform() == null ? "generic" : mai.getPlatform(),
 321                 mai.getArch() == null ? "generic" : mai.getArch(),
 322                 lastMod == null ? "n/a" : lastMod,
 323                 mai.getFileName() == null ? "n/a" : mai.getFileName()
 324                 );
 325         }
 326         return sw.toString();
 327     }
 328 
 329     private static String getMText(Module m) {
 330         return getMDText(m.getModuleDefinition());
 331     }
 332 
 333     private static String getMDText(ModuleDefinition md) {
 334         StringWriter sw = new StringWriter();
 335         PrintWriter pw = new PrintWriter(sw);
 336         pw.printf(MDFormat, md.getName(), md.getVersion());
 337         if (verboseFlag.isEnabled()) {
 338             pw.printf(MDFormatVerbose,



 339                      md.getRepository().getName());
 340         }
 341         return sw.toString();
 342     }
 343 
 344     void usageError() {
 345         usageError(msg);
 346     }
 347 
 348     static void usageError(Messenger msg) {
 349         if (usage == null) {
 350             StringBuilder ub = new StringBuilder(
 351                 "Usage: jrepo <command>\nwhere <command> includes:"); // XXX i18n
 352             for (Command c : commands.values()) {
 353                 String u = c.usage();
 354                 if (u != null) {
 355                     ub.append("\n    ").append(c.usage());
 356                 }
 357             }
 358             usage = ub.toString();


 415         }
 416 
 417         @Override
 418         int set(String[] args, int pos) throws IllegalArgumentException {
 419             int rc = super.set(args, pos);
 420             location = args[pos + 1];
 421             return rc + 1;
 422         }
 423 
 424         String getLocation() {
 425             return location;
 426         }
 427 
 428         @Override
 429         void reset() {
 430             super.reset();
 431             location = null;
 432         }
 433     }
 434 





































 435     /*
 436      * Command types: An abstract base class, plus one concrete class for
 437      * each Command.
 438      */
 439 
 440     /*
 441      * Represents a Command.
 442      */
 443     private static abstract class Command {
 444         private final String name;
 445 
 446         Command(String name) {
 447             this.name = name;
 448         }
 449 
 450         /** Adds this command to the given registry. */
 451         void register(Map<String, Command> registry) {
 452             registry.put(name, this);
 453         }
 454 
 455         void reset() {
 456             // Empty; subclasses can implement
 457         }
 458 
 459         // Flags differ from arguments in that they have the form "-X [opt]".
 460         // Flags appear in a command line before arguments.
 461 
 462         /** Parses the arguments particular to this command. */
 463         abstract boolean parseArgs(String[] args, Messenger msg);
 464 
 465         /**
 466          * Parse the Flags for this command.
 467          * @return number of flags found.
 468          * @throws IllegalArgumentException if an invalid flag is given.
 469          */
 470 /*        int parseFlags(String[] args, Map<Character, Flag> flags) {
 471             int rc = 0;
 472             int i = 0;
 473             while (i < args.length) {
 474                 String s = args[i];
 475                 if (s.length() == 2 && s.charAt(0) == '-') {
 476                     Flag f = flags.get(s.charAt(1));
 477                     if (f != null) {
 478                         int numConsumed = f.set(args, i);
 479                         i += numConsumed;  // Increases at each iteration.
 480                         rc += numConsumed; // Increases only when the arg is a flag.
 481                     } else {
 482                         throw new IllegalArgumentException("unrecognized flag: " // XXX i18n
 483                                                            + args[i]);
 484                     }
 485                 } if (s.charAt(0) == '-') {
 486                     throw new IllegalArgumentException("invalid flag: " // XXX i18n
 487                                                        + args[i]);
 488                 } else {
 489                     i++;
 490                 }
 491             }
 492             return rc;
 493         }
 494 */
 495         int parseFlags(String[] args, Map<Character, Flag> flags) {
 496             int rc = 0;
 497             int i = 0;
 498             while (i < args.length) {
 499                 String s = args[i];
 500                 if (s.charAt(0) == '-') {
 501                     if (s.length() == 2) {
 502                         Flag f = flags.get(s.charAt(1));
 503                         if (f != null) {
 504                             int numConsumed = f.set(args, i);
 505                             i += numConsumed;  // Increases at each iteration.
 506                             rc += numConsumed; // Increases only when the arg is a flag.
 507                         } else {
 508                             throw new IllegalArgumentException("unrecognized flag: " // XXX i18n
 509                                                                + args[i]);
 510                         }
 511                     } else {
 512                         throw new IllegalArgumentException("invalid flag: " // XXX i18n
 513                                                            + args[i]);
 514                     }
 515                 } else {
 516                     i++;
 517                 }
 518             }
 519             return rc;
 520         }
 521 
 522         /** Represents the actual behavior of the command. */
 523         abstract boolean run(Repository repo, Messenger msg);
 524 
 525         /** Prints command-specific usage message. */
 526         void usageError(Messenger msg) {
 527             msg.error("Synopsis for " + name + ": \n" + usage());
 528         }
 529 
 530         /**
 531          *Returns a usage string describing this command, or null if the
 532          * command is a synonym for another command.
 533          */
 534         abstract String usage();
 535 
 536         @Override
 537         public String toString() { return name; }
 538 
 539         /**
 540          * RepositoryVisitor types walk a parent chain of repositories, invoking
 541          * {@code doit} in each one.  The abstract base class provides the recursion;
 542          * each concrete subclass provides the per-repository behavior.  The
 543          * recursion is such that the bootstrap repository is visited first,
 544          * and the application repository is last.
 545          */
 546         abstract class RepositoryVisitor {
 547             abstract void doit(Repository repo, Messenger msg);
 548 
 549             void preVisit(Messenger msg) { }
 550 
 551             void postVisit(Messenger msg) { }


 564                 postVisit(msg);
 565             }
 566         }
 567     }
 568 
 569 
 570     /** Lists dependencies of a module. */
 571     private class DependenciesCommand extends Command {
 572         @SuppressWarnings("unchecked")
 573         private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
 574 
 575         private String name;
 576         private Version version;
 577 
 578         // For printing module name, version.
 579         private String moduleString;
 580 
 581         DependenciesCommand() {
 582             super("dependencies");
 583             javaseFlag.register(myFlags);

 584         }
 585 
 586         @Override
 587         void reset() {
 588             javaseFlag.reset();

 589         }
 590 
 591         @Override
 592         int parseFlags(String[] args, Map<Character, Flag> flags) {
 593             return super.parseFlags(args, myFlags);
 594         }
 595 
 596         boolean parseArgs(String[] args, Messenger msg) {
 597             if (args.length > 0) {
 598                 name = args[0];
 599             }
 600             if (args.length > 1) {
 601                 try {
 602                     version = Version.valueOf(args[1]);
 603                 } catch (IllegalArgumentException ex) {
 604                     return false;
 605                 }
 606             }
 607             if (args.length > 2) {
 608                 return false;
 609             }
 610 
 611             if (DEBUG) debug("name: " + name + " ver: " + version);


 612             return true;
 613         }
 614 
 615         boolean run(Repository repo, Messenger msg) {
 616             boolean rc = true;
 617             if (name != null) {
 618                 VersionConstraint vc = (version == null
 619                                         ? VersionConstraint.DEFAULT
 620                                         : version.toVersionConstraint());
 621                 printHeader(name, vc);
 622                 rc = depend(repo, name, vc);

 623                 printTrailer(null);
 624             } else {
 625                 for (ModuleArchiveInfo mai : repo.list()) {
 626                     reset();
 627                     String maiName = mai.getName();
 628                     VersionConstraint vc = mai.getVersion().toVersionConstraint();
 629                     printHeader(maiName, vc);
 630                     rc &= depend(repo, maiName, vc);

 631                     printTrailer("\n");
 632                 }
 633             }
 634             return rc;
 635         }
 636 
 637         boolean depend(Repository repo, String name, VersionConstraint constraint) {

 638             ImportTraverser traverser = new ImportTraverser();
 639             ImportTraverser.Visitor visitor = new ImportVisitor(traverser, msg);
 640             try {
 641                 traverser.traverse(visitor, repo, name, constraint);
 642                 if (traverser.traversedAny()) {
 643                     return true;
 644                 }
 645                 if (verboseFlag.isEnabled()) {
 646                     msg.error("Cannot find module " + name + " in " // XXX i18n
 647                               + getRepositoryText(repo));
 648                 }
 649                 return false;
 650             } catch (ModuleInitializationException ex) {
 651                 msg.error("Cannot instantiate module for " + name // XXX i18n
 652                           + ": " + ex);
 653                 return false;
 654             }
 655         }
 656 
 657 
 658         void printHeader(String name, VersionConstraint vc) {
 659             if (verboseFlag.isEnabled()) {
 660                 msg.println("Dependencies for " // XXXi18n
 661                             + name + "-" + vc + ":");
 662             }
 663         }
 664 
 665         void printTrailer(String s) {
 666             if (verboseFlag.isEnabled()) {
 667                 msg.print(s);
 668             }
 669         }
 670 
 671         String usage() {
 672             // XXX i18n
 673             return "dependencies [-v] [-r repositoryLocation] [moduleName [moduleVersion] ]\n"
 674                 + "        Lists all modules on which identified modules depend.\n"
 675                 + "        If no moduleName is given, lists dependencies of all"
 676                 + " module archives in the repository\n"
 677                 + "        which are instantiable on the current platform.";
 678         }
 679     }
 680 
 681 
 682     private static class ImportVisitor extends ImportTraverser.Visitor {
 683         private final Messenger msg;
 684 
 685         private static final String INDENT = "    ";
 686         private static final int INDENT_LENGTH = INDENT.length();
 687 
 688         private String indent = "";
 689 
 690         ImportVisitor(ImportTraverser traverser, Messenger msg) {
 691             super(traverser);
 692             this.msg = msg;
 693         }


 711         @Override
 712         protected void visit(Module m) {
 713             printModule(m);
 714         }
 715 
 716         @Override
 717         protected void postVisit(Module m) {
 718             if (javaseFlag.isEnabled() == false
 719                 && m.getModuleDefinition().getName().startsWith("java.se")) {
 720                 // empty
 721             } else {
 722                 indent = indent.substring(INDENT_LENGTH);
 723             }
 724         }
 725 
 726         void printModule(Module m) {
 727             msg.println(indent + getMText(m));
 728         }
 729     }
 730     
 731     
 732     /** Prints help. */
 733     private class HelpCommand extends Command {
 734         HelpCommand() {
 735             super("help");
 736         }
 737         
 738         boolean parseArgs(String[] args, Messenger msg) {
 739             return true;
 740         }
 741         
 742         boolean run(Repository repo, Messenger msg) {
 743             JRepo.this.usageError(msg);
 744             return true;
 745         }
 746         
 747         String usage() {
 748             return "help\n        Prints brief help text for all commands.\n"
 749                 +  "        Each command allows a -h option which gives help for that command.";
 750         }
 751     }
 752     
 753 
 754     /** Installs a JAM into a repository. */
 755     private class InstallCommand extends Command {
 756         @SuppressWarnings("unchecked")
 757         private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
 758 
 759         /** When used, prevents checking of installed module's dependencies. */
 760         private Flag quickInstallFlag = new Flag('q');
 761 
 762         private String jamName;
 763 
 764         InstallCommand() {
 765             super("install");
 766             quickInstallFlag.register(myFlags);
 767         }
 768 
 769         @Override
 770         void reset() {
 771             jamName = null;
 772             quickInstallFlag.reset();
 773         }
 774 
 775         @Override
 776         int parseFlags(String[] args, Map<Character, Flag> flags) {
 777             return super.parseFlags(args, myFlags);
 778         }
 779 
 780         boolean parseArgs(String[] args, Messenger msg) {
 781             boolean rc = false;
 782             if (!parentFlag.isEnabled()
 783                     && repositoryFlag.getLocation() != null
 784                     && args.length == 1) {
 785                 jamName = args[0];
 786                 rc = true;
 787             }
 788             return rc;
 789         }
 790 
 791         boolean run(Repository repo, Messenger msg) {
 792             if (repo != null) {
 793                 String jamURL = null;
 794                 File f = new File(jamName);
 795                 if (f.canRead()) {
 796                     try {
 797                         String path = f.getCanonicalPath();
 798                         // Ensure that path starts with a "/" (it does not on
 799                         // some systems, e.g. Windows).
 800                         if (!path.startsWith("/")) {
 801                             path = "/" + path;
 802                         }
 803                         jamURL = "file://" + path;
 804                     } catch (IOException ex) {
 805                         msg.error("Cannot install " + jamName + ": " + ex.getMessage());
 806                         return false;
 807                     }
 808                 } else {
 809                     jamURL = jamName;
 810                 }
 811                 try {
 812                     ModuleArchiveInfo mai = repo.install(new URL(jamURL).toURI());
 813                     if (verboseFlag.isEnabled()) {
 814                         msg.println("Installed " + jamName + ": " + getMAIText(mai));
 815                     }
 816                     if (!quickInstallFlag.isEnabled()) {
 817                         ModuleDefinition md = repo.find(
 818                             mai.getName(), mai.getVersion().toVersionConstraint());
 819                         if (md == null) {
 820                             msg.error("Warning: " + jamName
 821                                       + " was installed but cannot be found");
 822                         } else {
 823                             try {
 824                                 Module m = md.getModuleInstance();
 825                             } catch (ModuleInitializationException ex) {
 826                                 msg.error("Cannot install " + ex.getMessage());
 827                                 if (!repo.uninstall(mai)) {
 828                                     msg.error("Could not uninstall " + mai);
 829                                 }
 830                                 return false;
 831                             }
 832                         }
 833                     }
 834                     return true;
 835                 } catch (IllegalStateException ex) {
 836                     msg.error("Cannot install " + jamName + ": " + ex.getMessage());
 837                 } catch (URISyntaxException ex) {
 838                     msg.error("Cannot install " + jamName + ": no such file, or malformed URI");
 839                 } catch (MalformedURLException ex) {
 840                     msg.error("Cannot install " + jamName + ": no such file, or malformed URL");
 841                 } catch (IOException ex) {
 842                     msg.error("Cannot install " + jamName + ": " + ex.getMessage());
 843                 }
 844             }
 845             return false;
 846         }
 847 
 848         String usage() {
 849             return "install [-v] [-q] -r repositoryLocation jamFile | jamURL\n" // XXX i18n
 850                 +  "        Installs a module into a repository.";
 851         }
 852     }
 853 
 854     /** Uninstalls a module from a repository. */
 855     private class UninstallCommand extends Command {
 856         @SuppressWarnings("unchecked")
 857         private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
 858 
 859         private Flag forceFlag = new Flag('f');
 860 
 861         private Flag interactiveFlag = new Flag('i');
 862 
 863         private String moduleName;
 864 
 865         private Version version;
 866 
 867         private String platformBinding;
 868 
 869         UninstallCommand() {
 870             super("uninstall");


 952                 } else if (forceFlag.isEnabled()) {
 953                     if (DEBUG) debug("forced uninstall of multiple matches");
 954                     rc = true;
 955                     for (ModuleArchiveInfo mai : found) {
 956                         if (!uninstall(repo, mai, msg)) {
 957                             if (DEBUG) debug("uninstall failed for " + getMAIText(mai) + " in " + repo.toString());
 958                             rc = false;
 959                             break;
 960                         }
 961                     }
 962                 } else if (interactiveFlag.isEnabled()) {
 963                     rc = uninstallInteractive(repo, found, msg);
 964                 }
 965             }
 966             return rc;
 967         }
 968 
 969         String usage() {
 970             return "uninstall [-v] [-f | -i] -r repositoryLocation moduleName"// XXX i18n
 971                 + " [moduleVersion] [modulePlatformBinding]\n"
 972                 +  "        Removes a module from a repository, along with associated files.";
 973         }
 974 
 975         /** Uninstall the ModuleArchiveInfo from the Repository. */
 976         private boolean uninstall(Repository repo, ModuleArchiveInfo mai, Messenger msg) {
 977             boolean rc = false;
 978             if (DEBUG) debug("Uninstalling " + getMAIText(mai));
 979             try {
 980                 rc = repo.uninstall(mai);
 981                 if (verboseFlag.isEnabled()) {
 982                     if (rc) {
 983                         msg.println("Uninstalled " + getMAIText(mai)); // XXX i18n
 984                     } else {
 985                         msg.error("Failed to uninstall " + getMAIText(mai)); // XXX i18n
 986                     }
 987                 }
 988             } catch (Exception ex) {
 989                 msg.error("Exception while uninstalling " + getInfo()
 990                           + ": " + ex.getMessage());
 991             }
 992             return rc;


1063                             rc = true;
1064                         }
1065                     }
1066                 }
1067             }
1068             return rc;
1069         }
1070 
1071         private String getInfo() {
1072             String s = moduleName;
1073             if (version != null) {
1074                 s += " with version " + version;
1075             }
1076             if (platformBinding != null) {
1077                 s += " and platform-binding of " + platformBinding;
1078             }
1079             return s;
1080         }
1081     }
1082 
1083     /** Provides a way to sort ModuleArchiveInfo instances. */
1084     static class MAIComparator implements Comparator<ModuleArchiveInfo> {
1085         private static final MAIComparator instance = new MAIComparator();
1086 
1087         private MAIComparator() { }
1088 
1089         static MAIComparator getInstance() {
1090             return instance;
1091         }
1092 
1093         public int compare(ModuleArchiveInfo o1, ModuleArchiveInfo o2) {
1094             int rc = o1.getName().compareTo(o2.getName());
1095             if (rc != 0) {
1096                 return rc;
1097             }
1098 
1099             rc = o1.getVersion().compareTo(o2.getVersion());
1100             if (rc != 0) {
1101                 return rc;
1102             }
1103 
1104             rc = compareStrings(o1.getPlatform(), o2.getPlatform());
1105             if (rc != 0) {
1106                 return rc;
1107             }
1108 
1109             return compareStrings(o1.getArch(), o2.getArch());
1110         }
1111 
1112         // Compare two Strings.  If neither is null, compare them
1113         // lexicographically.  If both are null, return 0.  If the first is
1114         // null, return -1; else 1.
1115         private int compareStrings(String s1, String s2) {
1116             int rc = 0;
1117             if (s2 != null && s2 != null) {
1118                 rc = s1.compareTo(s2);
1119                 if (rc != 0) {
1120                     return rc;
1121                 }
1122             }
1123             if (s1 == null) {
1124                 return -1;
1125             } else if (s2 == null) {
1126                 return 1;
1127             }
1128             return 0;
1129         }
1130 
1131         @Override
1132         public boolean equals(Object o) {
1133             return this == o;
1134         }
1135     }
1136 
1137     /** Prints information about modules found in repositories. */
1138     private class ListCommand extends Command {
1139         @SuppressWarnings("unchecked")
1140         private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
1141 
1142         ListCommand() {
1143             super("list");
1144             parentFlag.register(myFlags);
1145         }
1146 
1147         @Override
1148         void reset() {
1149             parentFlag.reset();
1150         }
1151 
1152         @Override
1153         int parseFlags(String[] args, Map<Character, Flag> flags) {
1154             return super.parseFlags(args, myFlags);
1155         }
1156 


1165             }
1166             return rc;
1167         }
1168 
1169         boolean run(Repository repo, Messenger msg) {
1170             ListRepositoryVisitor visitor = new ListRepositoryVisitor();
1171             visitor.run(repo, msg);
1172             boolean found = visitor.wasFound();
1173             if (verboseFlag.isEnabled() && !found) {
1174                 if (moduleName != null) {
1175                     msg.error("Could not find module name starting with '"// XXX i18n
1176                               + moduleName + "'");
1177                 } else {
1178                     msg.error("Could not find any modules"); // XXX i18n
1179                 }
1180             }
1181             return found;
1182         }
1183 
1184         String usage() {
1185             return "list [-v] [-p] [-r repositoryLocation] [moduleName]\n"// XXX i18n
1186                 +  "        Lists the modules in the repository that match the given name.\n"
1187                 +  "        If no moduleName is given, lists all modules in the repository.";
1188         }
1189 
1190         class ListRepositoryVisitor extends RepositoryVisitor {
1191             private boolean found = false;
1192 
1193             boolean wasFound() { return found; }
1194 
1195             void doit(Repository repo, Messenger msg) {
1196                 boolean printedHeader = false;
1197                 List<ModuleArchiveInfo> maiList = repo.list();
1198                 if (maiList.size() == 0 && verboseFlag.isEnabled()) {
1199                     msg.println(getRepositoryText(repo));
1200                     msg.println("   empty");
1201                 } else {
1202                     TreeSet<ModuleArchiveInfo> sorted =
1203                         new TreeSet<ModuleArchiveInfo>(MAIComparator.getInstance());
1204                     sorted.addAll(repo.list());
1205                     for (ModuleArchiveInfo mai : sorted) {
1206                         if (moduleName == null || mai.getName().startsWith(moduleName)) {
1207                             if (!printedHeader) {
1208                                 msg.println(getRepositoryText(repo));
1209                                 msg.println(verboseFlag.isEnabled() ? maiHeadingVerbose : maiHeading);
1210                                 printedHeader = true;
1211                             }
1212                             msg.println(getMAIText(mai));
1213                             found = true;
1214                         }
1215                     }
1216                 }
1217             }
1218         }
1219     }
1220 
1221     /** Runs shallow and/or deep validation on one or more modules. */
1222     class ValidateCommand extends Command {
1223         @SuppressWarnings("unchecked")
1224         private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
1225 
1226         /** Indicates that deep validation should be run. */
1227         private Flag deepValidateFlag = new Flag('d');
1228 
1229         private String name;
1230         private Version version;
1231 
1232         ValidateCommand() {
1233             super("validate");
1234             deepValidateFlag.register(myFlags);
1235         }
1236 
1237         @Override
1238         void reset() {
1239             deepValidateFlag.reset();
1240         }
1241 
1242         @Override
1243         int parseFlags(String[] args, Map<Character, Flag> flags) {
1244             return super.parseFlags(args, myFlags);
1245         }
1246 
1247         boolean parseArgs(String[] args, Messenger msg) {
1248             if (args.length > 0) {
1249                 name = args[0];
1250             }
1251             if (args.length > 1) {
1252                 try {
1253                     version = Version.valueOf(args[1]);
1254                 } catch (IllegalArgumentException ex) {
1255                     return false;
1256                 }
1257             }
1258             if (args.length > 2) {
1259                 return false;
1260             }
1261 
1262             if (DEBUG) debug("name: " + name + " ver: " + version);
1263             return true;
1264         }
1265 
1266         boolean run(Repository repo, Messenger msg) {
1267             boolean rc = true;
1268             if (name != null) {
1269                 VersionConstraint vc = (version == null
1270                                         ? VersionConstraint.DEFAULT
1271                                         : version.toVersionConstraint());
1272 
1273                 List<ModuleDefinition> mdList = repo.find(Query.module(name, vc));
1274                 if (mdList.size() == 1) {
1275                     rc = validate(mdList.get(0), msg);
1276                 } else {
1277                     throw new IllegalArgumentException(
1278                         "more than one matching module found for name=" + name
1279                         + " version=" + vc);
1280                 }
1281             } else {
1282                 for (ModuleDefinition md : repo.findAll()) {
1283                     rc &= validate(md, msg);
1284                 }
1285             }
1286             return rc;
1287         }
1288 
1289         /** @return true if the module passes validation validation. */
1290         boolean validate(ModuleDefinition md, Messenger msg) {
1291             boolean rc = false;
1292             try {
1293                 Module m = md.getModuleInstance();
1294                 if (deepValidateFlag.isEnabled()) {
1295                     if (m.supportsDeepValidation()) {
1296                         try {
1297                             m.deepValidate();
1298                             if (verboseFlag.isEnabled()) {
1299                                 msg.println(
1300                                     "Module " + m + " passed deep validation"); // XXX i18n
1301                             }
1302                             rc = true;
1303                         } catch (ModuleInitializationException ex) {
1304                             msg.error(
1305                                 "Module " + m + " failed deep validation"); // XXX i18n
1306                         }
1307                     }
1308                 } else {
1309                     // Module was instantiated => passed shallow validation
1310                     if (verboseFlag.isEnabled()) {
1311                         msg.println(
1312                             "Module " + m + " passed shallow validation"); // XXX i18n
1313                     }
1314                     rc = true;
1315                 }
1316             } catch (ModuleInitializationException ex) {
1317                 msg.error("Could not initialize module for " + md // XXX i18n
1318                           + ": " + ex.getMessage());
1319             }
1320             return rc;
1321         }
1322         
1323         String usage() {
1324              // XXX i18n
1325             return "validate [-v] [-d] [-r repositoryLocation] [moduleName [moduleVersion] ]\n"
1326                 + "        Runs deep validation on the identified modules.\n"
1327                 + "        If no moduleName is given, validates all module"
1328                 + " archives in the repository.\n";
1329         }
1330     }
1331 }