< prev index next >

src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/StandardBundlerParam.java

Print this page




   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 jdk.jpackage.internal;
  27 
  28 import jdk.jpackage.internal.BundleParams;
  29 import jdk.jpackage.internal.AbstractAppImageBuilder;
  30 
  31 import java.io.File;
  32 import java.io.IOException;
  33 import java.io.StringReader;

  34 import java.nio.file.Files;
  35 import java.nio.file.Path;
  36 import java.nio.file.Paths;
  37 import java.text.MessageFormat;
  38 import java.util.ArrayList;
  39 import java.util.Arrays;
  40 import java.util.Collections;
  41 import java.util.Date;
  42 import java.util.HashMap;
  43 import java.util.HashSet;
  44 import java.util.LinkedHashSet;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Optional;
  48 import java.util.Properties;
  49 import java.util.ResourceBundle;
  50 import java.util.Set;
  51 import java.util.HashSet;
  52 import java.util.function.BiFunction;
  53 import java.util.function.Function;
  54 import java.util.jar.Attributes;
  55 import java.util.jar.JarFile;
  56 import java.util.jar.Manifest;
  57 import java.util.regex.Pattern;
  58 import java.util.stream.Collectors;

  59 
  60 /**
  61  * StandardBundlerParam
  62  *
  63  * A parameter to a bundler.
  64  *
  65  * Also contains static definitions of all of the common bundler parameters.
  66  * (additional platform specific and mode specific bundler parameters
  67  * are defined in each of the specific bundlers)
  68  *
  69  * Also contains static methods that operate on maps of parameters.
  70  */
  71 class StandardBundlerParam<T> extends BundlerParamInfo<T> {
  72 
  73     private static final ResourceBundle I18N = ResourceBundle.getBundle(
  74             "jdk.jpackage.internal.resources.MainResources");
  75     private static final String JAVABASEJMOD = "java.base.jmod";


  76 
  77     StandardBundlerParam(String id, Class<T> valueType,
  78             Function<Map<String, ? super Object>, T> defaultValueFunction,
  79             BiFunction<String, Map<String, ? super Object>, T> stringConverter)
  80     {
  81         this.id = id;
  82         this.valueType = valueType;
  83         this.defaultValueFunction = defaultValueFunction;
  84         this.stringConverter = stringConverter;
  85     }
  86 
  87     static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
  88             new StandardBundlerParam<>(
  89                     BundleParams.PARAM_APP_RESOURCES,
  90                     RelativeFileSet.class,
  91                     null, // no default.  Required parameter
  92                     null  // no string translation,
  93                           // tool must provide complex type
  94             );
  95 


 118                         }
 119                         else {
 120                             return value;
 121                         }
 122                     }
 123             );
 124 
 125     // note that each bundler is likely to replace this one with
 126     // their own converter
 127     static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
 128             new StandardBundlerParam<>(
 129                     Arguments.CLIOptions.MAIN_JAR.getId(),
 130                     RelativeFileSet.class,
 131                     params -> {
 132                         extractMainClassInfoFromAppResources(params);
 133                         return (RelativeFileSet) params.get("mainJar");
 134                     },
 135                     (s, p) -> getMainJar(s, p)
 136             );
 137 
 138     // TODO: test CLASSPATH jar manifest Attributet
 139     static final StandardBundlerParam<String> CLASSPATH =
 140             new StandardBundlerParam<>(
 141                     "classpath",
 142                     String.class,
 143                     params -> {
 144                         extractMainClassInfoFromAppResources(params);
 145                         String cp = (String) params.get("classpath");
 146                         return cp == null ? "" : cp;
 147                     },
 148                     (s, p) -> s.replace(File.pathSeparator, " ")
 149             );
 150 
 151     static final StandardBundlerParam<String> MAIN_CLASS =
 152             new StandardBundlerParam<>(
 153                     Arguments.CLIOptions.APPCLASS.getId(),
 154                     String.class,
 155                     params -> {
 156                         if (isRuntimeInstaller(params)) {
 157                             return null;
 158                         }
 159                         extractMainClassInfoFromAppResources(params);
 160                         String s = (String) params.get(
 161                                 BundleParams.PARAM_APPLICATION_CLASS);
 162                         if (s == null) {
 163                             s = JLinkBundlerHelper.getMainClass(params);

 164                         }
 165                         return s;
 166                     },
 167                     (s, p) -> s
 168             );
 169 
 170     static final StandardBundlerParam<File> PREDEFINED_RUNTIME_IMAGE =
 171             new StandardBundlerParam<>(
 172                     Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(),
 173                     File.class,
 174                     params -> null,
 175                     (s, p) -> new File(s)
 176             );
 177 
 178     static final StandardBundlerParam<String> APP_NAME =
 179             new StandardBundlerParam<>(
 180                     Arguments.CLIOptions.NAME.getId(),
 181                     String.class,
 182                     params -> {
 183                         String s = MAIN_CLASS.fetchFrom(params);


 222                             ? APP_NAME.fetchFrom(params)
 223                             : I18N.getString("param.description.default"),
 224                     (s, p) -> s
 225             );
 226 
 227     static final StandardBundlerParam<String> COPYRIGHT =
 228             new StandardBundlerParam<>(
 229                     Arguments.CLIOptions.COPYRIGHT.getId(),
 230                     String.class,
 231                     params -> MessageFormat.format(I18N.getString(
 232                             "param.copyright.default"), new Date()),
 233                     (s, p) -> s
 234             );
 235 
 236     @SuppressWarnings("unchecked")
 237     static final StandardBundlerParam<List<String>> ARGUMENTS =
 238             new StandardBundlerParam<>(
 239                     Arguments.CLIOptions.ARGUMENTS.getId(),
 240                     (Class<List<String>>) (Object) List.class,
 241                     params -> Collections.emptyList(),
 242                     (s, p) -> splitStringWithEscapes(s)
 243             );
 244 
 245     @SuppressWarnings("unchecked")
 246     static final StandardBundlerParam<List<String>> JAVA_OPTIONS =
 247             new StandardBundlerParam<>(
 248                     Arguments.CLIOptions.JAVA_OPTIONS.getId(),
 249                     (Class<List<String>>) (Object) List.class,
 250                     params -> Collections.emptyList(),
 251                     (s, p) -> Arrays.asList(s.split("\n\n"))
 252             );
 253 
 254     // note that each bundler is likely to replace this one with
 255     // their own converter
 256     static final StandardBundlerParam<String> VERSION =
 257             new StandardBundlerParam<>(
 258                     Arguments.CLIOptions.VERSION.getId(),
 259                     String.class,
 260                     params -> I18N.getString("param.version.default"),








 261                     (s, p) -> s
 262             );
 263 
 264     @SuppressWarnings("unchecked")
 265     public static final StandardBundlerParam<String> LICENSE_FILE =
 266             new StandardBundlerParam<>(
 267                     Arguments.CLIOptions.LICENSE_FILE.getId(),
 268                     String.class,
 269                     params -> null,
 270                     (s, p) -> s
 271             );
 272 
 273     static final StandardBundlerParam<File> TEMP_ROOT =
 274             new StandardBundlerParam<>(
 275                     Arguments.CLIOptions.TEMP_ROOT.getId(),
 276                     File.class,
 277                     params -> {
 278                         try {
 279                             return Files.createTempDirectory(
 280                                     "jdk.jpackage").toFile();
 281                         } catch (IOException ioe) {
 282                             return null;
 283                         }
 284                     },
 285                     (s, p) -> new File(s)
 286             );
 287 
 288     public static final StandardBundlerParam<File> CONFIG_ROOT =
 289             new StandardBundlerParam<>(
 290                 "configRoot",
 291                 File.class,
 292                 params -> {
 293                     File root =
 294                             new File(TEMP_ROOT.fetchFrom(params), "config");
 295                     root.mkdirs();
 296                     return root;
 297                 },
 298                 (s, p) -> null
 299             );
 300 
 301     static final StandardBundlerParam<String> IDENTIFIER =
 302             new StandardBundlerParam<>(
 303                     Arguments.CLIOptions.IDENTIFIER.getId(),
 304                     String.class,
 305                     params -> {
 306                         String s = MAIN_CLASS.fetchFrom(params);
 307                         if (s == null) return null;
 308 
 309                         int idx = s.lastIndexOf(".");
 310                         if (idx >= 1) {
 311                             return s.substring(0, idx);
 312                         }
 313                         return s;
 314                     },
 315                     (s, p) -> s
 316             );
 317 










 318     static final StandardBundlerParam<Boolean> VERBOSE  =
 319             new StandardBundlerParam<>(
 320                     Arguments.CLIOptions.VERBOSE.getId(),
 321                     Boolean.class,
 322                     params -> false,
 323                     // valueOf(null) is false, and we actually do want null
 324                     (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
 325                             true : Boolean.valueOf(s)
 326             );
 327 
 328     static final StandardBundlerParam<File> RESOURCE_DIR =
 329             new StandardBundlerParam<>(
 330                     Arguments.CLIOptions.RESOURCE_DIR.getId(),
 331                     File.class,
 332                     params -> null,
 333                     (s, p) -> new File(s)
 334             );
 335 
 336     static final BundlerParamInfo<String> INSTALL_DIR =
 337             new StandardBundlerParam<>(


 456                     });
 457 
 458     @SuppressWarnings("unchecked")
 459     static final BundlerParamInfo<Set<String>> ADD_MODULES =
 460             new StandardBundlerParam<>(
 461                     Arguments.CLIOptions.ADD_MODULES.getId(),
 462                     (Class<Set<String>>) (Object) Set.class,
 463                     p -> new LinkedHashSet<String>(),
 464                     (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
 465             );
 466 
 467     @SuppressWarnings("unchecked")
 468     static final BundlerParamInfo<Set<String>> LIMIT_MODULES =
 469             new StandardBundlerParam<>(
 470                     "limit-modules",
 471                     (Class<Set<String>>) (Object) Set.class,
 472                     p -> new LinkedHashSet<String>(),
 473                     (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
 474             );
 475 
 476     static boolean isRuntimeInstaller(Map<String, ? super Object> p) {
 477         if (p.containsKey(MODULE.getID()) ||
 478                 p.containsKey(MAIN_JAR.getID()) ||
 479                 p.containsKey(PREDEFINED_APP_IMAGE.getID())) {
 480             return false; // we are building or are given an application
 481         }
 482         // runtime installer requires --runtime-image, if this is false
 483         // here then we should have thrown error validating args.
 484         return p.containsKey(PREDEFINED_RUNTIME_IMAGE.getID());
 485     }
 486 
 487     static File getPredefinedAppImage(Map<String, ? super Object> p) {
 488         File applicationImage = null;
 489         if (PREDEFINED_APP_IMAGE.fetchFrom(p) != null) {
 490             applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(p);
 491             Log.debug("Using App Image from " + applicationImage);
 492             if (!applicationImage.exists()) {
 493                 throw new RuntimeException(
 494                         MessageFormat.format(I18N.getString(
 495                                 "message.app-image-dir-does-not-exist"),
 496                                 PREDEFINED_APP_IMAGE.getID(),
 497                                 applicationImage.toString()));
 498             }
 499         }
 500         return applicationImage;
 501     }
 502 
 503     static void copyPredefinedRuntimeImage(
 504             Map<String, ? super Object> p,
 505             AbstractAppImageBuilder appBuilder)
 506             throws IOException , ConfigException {
 507         File image = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p);
 508         if (!image.exists()) {
 509             throw new ConfigException(
 510                     MessageFormat.format(I18N.getString(
 511                     "message.runtime-image-dir-does-not-exist"),
 512                     PREDEFINED_RUNTIME_IMAGE.getID(),
 513                     image.toString()),
 514                     MessageFormat.format(I18N.getString(
 515                     "message.runtime-image-dir-does-not-exist.advice"),
 516                     PREDEFINED_RUNTIME_IMAGE.getID()));
 517         }

 518         // copy whole runtime, need to skip jmods and src.zip
 519         final List<String> excludes = Arrays.asList("jmods", "src.zip");
 520         IOUtils.copyRecursive(image.toPath(), appBuilder.getRoot(), excludes);
 521 
 522         // if module-path given - copy modules to appDir/mods
 523         List<Path> modulePath =
 524                 StandardBundlerParam.MODULE_PATH.fetchFrom(p);
 525         List<Path> defaultModulePath = getDefaultModulePath();
 526         Path dest = appBuilder.getAppModsDir();
 527 
 528         if (dest != null) {
 529             for (Path mp : modulePath) {
 530                 if (!defaultModulePath.contains(mp)) {
 531                     Files.createDirectories(dest);
 532                     IOUtils.copyRecursive(mp, dest);
 533                 }
 534             }
 535         }
 536 
 537         appBuilder.prepareApplicationFiles();
 538     }
 539 
 540     static void extractMainClassInfoFromAppResources(
 541             Map<String, ? super Object> params) {
 542         boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
 543         boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
 544         boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
 545         boolean hasModule = params.containsKey(MODULE.getID());
 546 
 547         if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule ||
 548                 isRuntimeInstaller(params)) {
 549             return;
 550         }
 551 
 552         // it's a pair.
 553         // The [0] is the srcdir [1] is the file relative to sourcedir
 554         List<String[]> filesToCheck = new ArrayList<>();
 555 
 556         if (hasMainJar) {
 557             RelativeFileSet rfs = MAIN_JAR.fetchFrom(params);


 614                                     cp == null ? "" : cp);
 615                         }
 616                         break;
 617                     }
 618                 }
 619             } catch (IOException ignore) {
 620                 ignore.printStackTrace();
 621             }
 622         }
 623     }
 624 
 625     static void validateMainClassInfoFromAppResources(
 626             Map<String, ? super Object> params) throws ConfigException {
 627         boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
 628         boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
 629         boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
 630         boolean hasModule = params.containsKey(MODULE.getID());
 631         boolean hasAppImage = params.containsKey(PREDEFINED_APP_IMAGE.getID());
 632 
 633         if (hasMainClass && hasMainJar && hasMainJarClassPath ||
 634                hasModule || hasAppImage || isRuntimeInstaller(params)) {
 635             return;
 636         }
 637 





 638         extractMainClassInfoFromAppResources(params);
 639 
 640         if (!params.containsKey(MAIN_CLASS.getID())) {
 641             if (hasMainJar) {
 642                 throw new ConfigException(
 643                         MessageFormat.format(I18N.getString(
 644                         "error.no-main-class-with-main-jar"),
 645                         MAIN_JAR.fetchFrom(params)),
 646                         MessageFormat.format(I18N.getString(
 647                         "error.no-main-class-with-main-jar.advice"),
 648                         MAIN_JAR.fetchFrom(params)));
 649             } else {
 650                 throw new ConfigException(
 651                         I18N.getString("error.no-main-class"),
 652                         I18N.getString("error.no-main-class.advice"));
 653             }
 654         }
 655     }
 656 
 657     private static List<String> splitStringWithEscapes(String s) {
 658         List<String> l = new ArrayList<>();
 659         StringBuilder current = new StringBuilder();
 660         boolean quoted = false;
 661         boolean escaped = false;
 662         for (char c : s.toCharArray()) {
 663             if (escaped) {
 664                 current.append(c);
 665             } else if ('"' == c) {
 666                 quoted = !quoted;
 667             } else if (!quoted && Character.isWhitespace(c)) {
 668                 l.add(current.toString());
 669                 current = new StringBuilder();
 670             } else {
 671                 current.append(c);
 672             }
 673         }
 674         l.add(current.toString());
 675         return l;
 676     }
 677 
 678     private static List<RelativeFileSet>
 679             createAppResourcesListFromString(String s,
 680             Map<String, ? super Object> objectObjectMap) {
 681         List<RelativeFileSet> result = new ArrayList<>();
 682         for (String path : s.split("[:;]")) {
 683             File f = new File(path);
 684             if (f.getName().equals("*") || path.endsWith("/") ||
 685                     path.endsWith("\\")) {
 686                 if (f.getName().equals("*")) {
 687                     f = f.getParentFile();
 688                 }
 689                 Set<File> theFiles = new HashSet<>();
 690                 try {
 691                     Files.walk(f.toPath())
 692                             .filter(Files::isRegularFile)
 693                             .forEach(p -> theFiles.add(p.toFile()));

 694                 } catch (IOException e) {
 695                     e.printStackTrace();
 696                 }
 697                 result.add(new RelativeFileSet(f, theFiles));
 698             } else {
 699                 result.add(new RelativeFileSet(f.getParentFile(),
 700                         Collections.singleton(f)));
 701             }
 702         }
 703         return result;
 704     }
 705 
 706     private static RelativeFileSet getMainJar(
 707             String mainJarValue, Map<String, ? super Object> params) {
 708         for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) {
 709             File appResourcesRoot = rfs.getBaseDirectory();
 710             File mainJarFile = new File(appResourcesRoot, mainJarValue);
 711 
 712             if (mainJarFile.exists()) {
 713                 return new RelativeFileSet(appResourcesRoot,


 750             result.add(jdkModulePath);
 751         }
 752         else {
 753             // On a developer build the JDK Home isn't where we expect it
 754             // relative to the jmods directory. Do some extra
 755             // processing to find it.
 756             Map<String, String> env = System.getenv();
 757 
 758             if (env.containsKey("JDK_HOME")) {
 759                 jdkModulePath = Paths.get(env.get("JDK_HOME"),
 760                         ".." + File.separator + "images"
 761                         + File.separator + "jmods").toAbsolutePath();
 762 
 763                 if (jdkModulePath != null && Files.exists(jdkModulePath)) {
 764                     result.add(jdkModulePath);
 765                 }
 766             }
 767         }
 768 
 769         return result;


















 770     }
 771 }


   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 jdk.incubator.jpackage.internal;



  27 
  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.lang.module.ModuleDescriptor;
  31 import java.lang.module.ModuleDescriptor.Version;
  32 import java.nio.file.Files;
  33 import java.nio.file.Path;
  34 import java.nio.file.Paths;
  35 import java.text.MessageFormat;
  36 import java.util.ArrayList;
  37 import java.util.Arrays;
  38 import java.util.Collections;
  39 import java.util.Date;


  40 import java.util.LinkedHashSet;
  41 import java.util.List;
  42 import java.util.Map;
  43 import java.util.Optional;

  44 import java.util.ResourceBundle;
  45 import java.util.Set;
  46 import java.util.HashSet;
  47 import java.util.function.BiFunction;
  48 import java.util.function.Function;
  49 import java.util.jar.Attributes;
  50 import java.util.jar.JarFile;
  51 import java.util.jar.Manifest;

  52 import java.util.stream.Collectors;
  53 import java.util.stream.Stream;
  54 
  55 /**
  56  * StandardBundlerParam
  57  *
  58  * A parameter to a bundler.
  59  *
  60  * Also contains static definitions of all of the common bundler parameters.
  61  * (additional platform specific and mode specific bundler parameters
  62  * are defined in each of the specific bundlers)
  63  *
  64  * Also contains static methods that operate on maps of parameters.
  65  */
  66 class StandardBundlerParam<T> extends BundlerParamInfo<T> {
  67 
  68     private static final ResourceBundle I18N = ResourceBundle.getBundle(
  69             "jdk.incubator.jpackage.internal.resources.MainResources");
  70     private static final String JAVABASEJMOD = "java.base.jmod";
  71     private final static String DEFAULT_VERSION = "1.0";
  72     private final static String DEFAULT_RELEASE = "1";
  73 
  74     StandardBundlerParam(String id, Class<T> valueType,
  75             Function<Map<String, ? super Object>, T> defaultValueFunction,
  76             BiFunction<String, Map<String, ? super Object>, T> stringConverter)
  77     {
  78         this.id = id;
  79         this.valueType = valueType;
  80         this.defaultValueFunction = defaultValueFunction;
  81         this.stringConverter = stringConverter;
  82     }
  83 
  84     static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
  85             new StandardBundlerParam<>(
  86                     BundleParams.PARAM_APP_RESOURCES,
  87                     RelativeFileSet.class,
  88                     null, // no default.  Required parameter
  89                     null  // no string translation,
  90                           // tool must provide complex type
  91             );
  92 


 115                         }
 116                         else {
 117                             return value;
 118                         }
 119                     }
 120             );
 121 
 122     // note that each bundler is likely to replace this one with
 123     // their own converter
 124     static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
 125             new StandardBundlerParam<>(
 126                     Arguments.CLIOptions.MAIN_JAR.getId(),
 127                     RelativeFileSet.class,
 128                     params -> {
 129                         extractMainClassInfoFromAppResources(params);
 130                         return (RelativeFileSet) params.get("mainJar");
 131                     },
 132                     (s, p) -> getMainJar(s, p)
 133             );
 134 

 135     static final StandardBundlerParam<String> CLASSPATH =
 136             new StandardBundlerParam<>(
 137                     "classpath",
 138                     String.class,
 139                     params -> {
 140                         extractMainClassInfoFromAppResources(params);
 141                         String cp = (String) params.get("classpath");
 142                         return cp == null ? "" : cp;
 143                     },
 144                     (s, p) -> s
 145             );
 146 
 147     static final StandardBundlerParam<String> MAIN_CLASS =
 148             new StandardBundlerParam<>(
 149                     Arguments.CLIOptions.APPCLASS.getId(),
 150                     String.class,
 151                     params -> {
 152                         if (isRuntimeInstaller(params)) {
 153                             return null;
 154                         }
 155                         extractMainClassInfoFromAppResources(params);
 156                         String s = (String) params.get(
 157                                 BundleParams.PARAM_APPLICATION_CLASS);
 158                         if (s == null) {
 159                             s = JLinkBundlerHelper.getMainClassFromModule(
 160                                     params);
 161                         }
 162                         return s;
 163                     },
 164                     (s, p) -> s
 165             );
 166 
 167     static final StandardBundlerParam<File> PREDEFINED_RUNTIME_IMAGE =
 168             new StandardBundlerParam<>(
 169                     Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(),
 170                     File.class,
 171                     params -> null,
 172                     (s, p) -> new File(s)
 173             );
 174 
 175     static final StandardBundlerParam<String> APP_NAME =
 176             new StandardBundlerParam<>(
 177                     Arguments.CLIOptions.NAME.getId(),
 178                     String.class,
 179                     params -> {
 180                         String s = MAIN_CLASS.fetchFrom(params);


 219                             ? APP_NAME.fetchFrom(params)
 220                             : I18N.getString("param.description.default"),
 221                     (s, p) -> s
 222             );
 223 
 224     static final StandardBundlerParam<String> COPYRIGHT =
 225             new StandardBundlerParam<>(
 226                     Arguments.CLIOptions.COPYRIGHT.getId(),
 227                     String.class,
 228                     params -> MessageFormat.format(I18N.getString(
 229                             "param.copyright.default"), new Date()),
 230                     (s, p) -> s
 231             );
 232 
 233     @SuppressWarnings("unchecked")
 234     static final StandardBundlerParam<List<String>> ARGUMENTS =
 235             new StandardBundlerParam<>(
 236                     Arguments.CLIOptions.ARGUMENTS.getId(),
 237                     (Class<List<String>>) (Object) List.class,
 238                     params -> Collections.emptyList(),
 239                     (s, p) -> null
 240             );
 241 
 242     @SuppressWarnings("unchecked")
 243     static final StandardBundlerParam<List<String>> JAVA_OPTIONS =
 244             new StandardBundlerParam<>(
 245                     Arguments.CLIOptions.JAVA_OPTIONS.getId(),
 246                     (Class<List<String>>) (Object) List.class,
 247                     params -> Collections.emptyList(),
 248                     (s, p) -> Arrays.asList(s.split("\n\n"))
 249             );
 250 
 251     // note that each bundler is likely to replace this one with
 252     // their own converter
 253     static final StandardBundlerParam<String> VERSION =
 254             new StandardBundlerParam<>(
 255                     Arguments.CLIOptions.VERSION.getId(),
 256                     String.class,
 257                     params -> getDefaultAppVersion(params),
 258                     (s, p) -> s
 259             );
 260 
 261     static final StandardBundlerParam<String> RELEASE =
 262             new StandardBundlerParam<>(
 263                     Arguments.CLIOptions.RELEASE.getId(),
 264                     String.class,
 265                     params -> DEFAULT_RELEASE,
 266                     (s, p) -> s
 267             );
 268 
 269     @SuppressWarnings("unchecked")
 270     public static final StandardBundlerParam<String> LICENSE_FILE =
 271             new StandardBundlerParam<>(
 272                     Arguments.CLIOptions.LICENSE_FILE.getId(),
 273                     String.class,
 274                     params -> null,
 275                     (s, p) -> s
 276             );
 277 
 278     static final StandardBundlerParam<File> TEMP_ROOT =
 279             new StandardBundlerParam<>(
 280                     Arguments.CLIOptions.TEMP_ROOT.getId(),
 281                     File.class,
 282                     params -> {
 283                         try {
 284                             return Files.createTempDirectory(
 285                                     "jdk.incubator.jpackage").toFile();
 286                         } catch (IOException ioe) {
 287                             return null;
 288                         }
 289                     },
 290                     (s, p) -> new File(s)
 291             );
 292 
 293     public static final StandardBundlerParam<File> CONFIG_ROOT =
 294             new StandardBundlerParam<>(
 295                 "configRoot",
 296                 File.class,
 297                 params -> {
 298                     File root =
 299                             new File(TEMP_ROOT.fetchFrom(params), "config");
 300                     root.mkdirs();
 301                     return root;
 302                 },
 303                 (s, p) -> null
 304             );
 305 
 306     static final StandardBundlerParam<String> IDENTIFIER =
 307             new StandardBundlerParam<>(
 308                     "identifier.default",
 309                     String.class,
 310                     params -> {
 311                         String s = MAIN_CLASS.fetchFrom(params);
 312                         if (s == null) return null;
 313 
 314                         int idx = s.lastIndexOf(".");
 315                         if (idx >= 1) {
 316                             return s.substring(0, idx);
 317                         }
 318                         return s;
 319                     },
 320                     (s, p) -> s
 321             );
 322 
 323     static final StandardBundlerParam<Boolean> BIND_SERVICES =
 324             new StandardBundlerParam<>(
 325                     Arguments.CLIOptions.BIND_SERVICES.getId(),
 326                     Boolean.class,
 327                     params -> false,
 328                     (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
 329                             true : Boolean.valueOf(s)
 330             );
 331 
 332 
 333     static final StandardBundlerParam<Boolean> VERBOSE  =
 334             new StandardBundlerParam<>(
 335                     Arguments.CLIOptions.VERBOSE.getId(),
 336                     Boolean.class,
 337                     params -> false,
 338                     // valueOf(null) is false, and we actually do want null
 339                     (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
 340                             true : Boolean.valueOf(s)
 341             );
 342 
 343     static final StandardBundlerParam<File> RESOURCE_DIR =
 344             new StandardBundlerParam<>(
 345                     Arguments.CLIOptions.RESOURCE_DIR.getId(),
 346                     File.class,
 347                     params -> null,
 348                     (s, p) -> new File(s)
 349             );
 350 
 351     static final BundlerParamInfo<String> INSTALL_DIR =
 352             new StandardBundlerParam<>(


 471                     });
 472 
 473     @SuppressWarnings("unchecked")
 474     static final BundlerParamInfo<Set<String>> ADD_MODULES =
 475             new StandardBundlerParam<>(
 476                     Arguments.CLIOptions.ADD_MODULES.getId(),
 477                     (Class<Set<String>>) (Object) Set.class,
 478                     p -> new LinkedHashSet<String>(),
 479                     (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
 480             );
 481 
 482     @SuppressWarnings("unchecked")
 483     static final BundlerParamInfo<Set<String>> LIMIT_MODULES =
 484             new StandardBundlerParam<>(
 485                     "limit-modules",
 486                     (Class<Set<String>>) (Object) Set.class,
 487                     p -> new LinkedHashSet<String>(),
 488                     (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
 489             );
 490 
 491     static boolean isRuntimeInstaller(Map<String, ? super Object> params) {
 492         if (params.containsKey(MODULE.getID()) ||
 493                 params.containsKey(MAIN_JAR.getID()) ||
 494                 params.containsKey(PREDEFINED_APP_IMAGE.getID())) {
 495             return false; // we are building or are given an application
 496         }
 497         // runtime installer requires --runtime-image, if this is false
 498         // here then we should have thrown error validating args.
 499         return params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID());
 500     }
 501 
 502     static File getPredefinedAppImage(Map<String, ? super Object> params) {
 503         File applicationImage = null;
 504         if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) {
 505             applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params);

 506             if (!applicationImage.exists()) {
 507                 throw new RuntimeException(
 508                         MessageFormat.format(I18N.getString(
 509                                 "message.app-image-dir-does-not-exist"),
 510                                 PREDEFINED_APP_IMAGE.getID(),
 511                                 applicationImage.toString()));
 512             }
 513         }
 514         return applicationImage;
 515     }
 516 
 517     static void copyPredefinedRuntimeImage(
 518             Map<String, ? super Object> params,
 519             AbstractAppImageBuilder appBuilder)
 520             throws IOException , ConfigException {
 521         File topImage = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params);
 522         if (!topImage.exists()) {
 523             throw new ConfigException(
 524                     MessageFormat.format(I18N.getString(
 525                     "message.runtime-image-dir-does-not-exist"),
 526                     PREDEFINED_RUNTIME_IMAGE.getID(),
 527                     topImage.toString()),
 528                     MessageFormat.format(I18N.getString(
 529                     "message.runtime-image-dir-does-not-exist.advice"),
 530                     PREDEFINED_RUNTIME_IMAGE.getID()));
 531         }
 532         File image = appBuilder.getRuntimeImageDir(topImage);
 533         // copy whole runtime, need to skip jmods and src.zip
 534         final List<String> excludes = Arrays.asList("jmods", "src.zip");
 535         IOUtils.copyRecursive(image.toPath(), appBuilder.getRuntimeRoot(), excludes);
 536 
 537         // if module-path given - copy modules to appDir/mods
 538         List<Path> modulePath =
 539                 StandardBundlerParam.MODULE_PATH.fetchFrom(params);
 540         List<Path> defaultModulePath = getDefaultModulePath();
 541         Path dest = appBuilder.getAppModsDir();
 542 
 543         if (dest != null) {
 544             for (Path mp : modulePath) {
 545                 if (!defaultModulePath.contains(mp)) {
 546                     Files.createDirectories(dest);
 547                     IOUtils.copyRecursive(mp, dest);
 548                 }
 549             }
 550         }
 551 
 552         appBuilder.prepareApplicationFiles(params);
 553     }
 554 
 555     static void extractMainClassInfoFromAppResources(
 556             Map<String, ? super Object> params) {
 557         boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
 558         boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
 559         boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
 560         boolean hasModule = params.containsKey(MODULE.getID());
 561 
 562         if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule ||
 563                 isRuntimeInstaller(params)) {
 564             return;
 565         }
 566 
 567         // it's a pair.
 568         // The [0] is the srcdir [1] is the file relative to sourcedir
 569         List<String[]> filesToCheck = new ArrayList<>();
 570 
 571         if (hasMainJar) {
 572             RelativeFileSet rfs = MAIN_JAR.fetchFrom(params);


 629                                     cp == null ? "" : cp);
 630                         }
 631                         break;
 632                     }
 633                 }
 634             } catch (IOException ignore) {
 635                 ignore.printStackTrace();
 636             }
 637         }
 638     }
 639 
 640     static void validateMainClassInfoFromAppResources(
 641             Map<String, ? super Object> params) throws ConfigException {
 642         boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
 643         boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
 644         boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
 645         boolean hasModule = params.containsKey(MODULE.getID());
 646         boolean hasAppImage = params.containsKey(PREDEFINED_APP_IMAGE.getID());
 647 
 648         if (hasMainClass && hasMainJar && hasMainJarClassPath ||
 649                hasAppImage || isRuntimeInstaller(params)) {
 650             return;
 651         }
 652         if (hasModule) {
 653             if (JLinkBundlerHelper.getMainClassFromModule(params) == null) {
 654                 throw new ConfigException(
 655                         I18N.getString("ERR_NoMainClass"), null);
 656             }
 657         } else {
 658             extractMainClassInfoFromAppResources(params);
 659 
 660             if (!params.containsKey(MAIN_CLASS.getID())) {
 661                 if (hasMainJar) {
 662                     throw new ConfigException(
 663                             MessageFormat.format(I18N.getString(
 664                             "error.no-main-class-with-main-jar"),
 665                             MAIN_JAR.fetchFrom(params)),
 666                             MessageFormat.format(I18N.getString(
 667                             "error.no-main-class-with-main-jar.advice"),
 668                             MAIN_JAR.fetchFrom(params)));
 669                 } else {
 670                     throw new ConfigException(
 671                             I18N.getString("error.no-main-class"),
 672                             I18N.getString("error.no-main-class.advice"));
 673                 }
 674             }
 675         }




















 676     }
 677 
 678     private static List<RelativeFileSet>
 679             createAppResourcesListFromString(String s,
 680             Map<String, ? super Object> objectObjectMap) {
 681         List<RelativeFileSet> result = new ArrayList<>();
 682         for (String path : s.split("[:;]")) {
 683             File f = new File(path);
 684             if (f.getName().equals("*") || path.endsWith("/") ||
 685                     path.endsWith("\\")) {
 686                 if (f.getName().equals("*")) {
 687                     f = f.getParentFile();
 688                 }
 689                 Set<File> theFiles = new HashSet<>();
 690                 try {
 691                     try (Stream<Path> stream = Files.walk(f.toPath())) {
 692                         stream.filter(Files::isRegularFile)
 693                                 .forEach(p -> theFiles.add(p.toFile()));
 694                     }
 695                 } catch (IOException e) {
 696                     e.printStackTrace();
 697                 }
 698                 result.add(new RelativeFileSet(f, theFiles));
 699             } else {
 700                 result.add(new RelativeFileSet(f.getParentFile(),
 701                         Collections.singleton(f)));
 702             }
 703         }
 704         return result;
 705     }
 706 
 707     private static RelativeFileSet getMainJar(
 708             String mainJarValue, Map<String, ? super Object> params) {
 709         for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) {
 710             File appResourcesRoot = rfs.getBaseDirectory();
 711             File mainJarFile = new File(appResourcesRoot, mainJarValue);
 712 
 713             if (mainJarFile.exists()) {
 714                 return new RelativeFileSet(appResourcesRoot,


 751             result.add(jdkModulePath);
 752         }
 753         else {
 754             // On a developer build the JDK Home isn't where we expect it
 755             // relative to the jmods directory. Do some extra
 756             // processing to find it.
 757             Map<String, String> env = System.getenv();
 758 
 759             if (env.containsKey("JDK_HOME")) {
 760                 jdkModulePath = Paths.get(env.get("JDK_HOME"),
 761                         ".." + File.separator + "images"
 762                         + File.separator + "jmods").toAbsolutePath();
 763 
 764                 if (jdkModulePath != null && Files.exists(jdkModulePath)) {
 765                     result.add(jdkModulePath);
 766                 }
 767             }
 768         }
 769 
 770         return result;
 771     }
 772 
 773     static String getDefaultAppVersion(Map<String, ? super Object> params) {
 774         String appVersion = DEFAULT_VERSION;
 775 
 776         ModuleDescriptor descriptor = JLinkBundlerHelper.getMainModuleDescription(params);
 777         if (descriptor != null) {
 778             Optional<Version> oversion = descriptor.version();
 779             if (oversion.isPresent()) {
 780                 Log.verbose(MessageFormat.format(I18N.getString(
 781                         "message.module-version"),
 782                         oversion.get().toString(),
 783                         JLinkBundlerHelper.getMainModule(params)));
 784                 appVersion = oversion.get().toString();
 785             }
 786         }
 787 
 788         return appVersion;
 789     }
 790 }
< prev index next >