1 /*
   2  * Copyright (c) 2014, 2015, 2016 Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  */
   5 package com.oracle.appbundlers.utils;
   6 
   7 import static com.oracle.appbundlers.utils.Config.CONFIG_INSTANCE;
   8 import static java.lang.String.format;
   9 import static java.nio.file.Files.newDirectoryStream;
  10 import static java.util.stream.Collectors.joining;
  11 import static java.util.stream.Collectors.toList;
  12 
  13 import java.io.ByteArrayOutputStream;
  14 import java.io.File;
  15 import java.io.IOException;
  16 import java.nio.file.DirectoryStream;
  17 import java.nio.file.FileVisitResult;
  18 import java.nio.file.Files;
  19 import java.nio.file.Path;
  20 import java.nio.file.Paths;
  21 import java.nio.file.SimpleFileVisitor;
  22 import java.nio.file.attribute.BasicFileAttributes;
  23 import java.util.ArrayList;
  24 import java.util.Arrays;
  25 import java.util.Collections;
  26 import java.util.HashSet;
  27 import java.util.List;
  28 import java.util.Map;
  29 import java.util.Map.Entry;
  30 import java.util.Set;
  31 import java.util.concurrent.ExecutionException;
  32 import java.util.jar.Attributes;
  33 import java.util.jar.JarEntry;
  34 import java.util.jar.JarFile;
  35 import java.util.jar.JarOutputStream;
  36 import java.util.jar.Manifest;
  37 import java.util.logging.Level;
  38 import java.util.logging.Logger;
  39 import java.util.stream.Collectors;
  40 import java.util.stream.Stream;
  41 import java.util.zip.ZipEntry;
  42 
  43 import javax.tools.JavaCompiler;
  44 import javax.tools.ToolProvider;
  45 
  46 import javafx.util.Pair;
  47 
  48 /**
  49  * @author Dmitry Ginzburg <dmitry.x.ginzburg@oracle.com>
  50  */
  51 public class AppWrapper implements Constants {
  52 
  53     private static final Logger LOG = Logger
  54             .getLogger(AppWrapper.class.getName());
  55 
  56     private Path workDir;
  57     private final List<Source> sources;
  58     private final String mainJar;
  59     private final String mainClass;
  60 
  61     private String paramsForJarsCompilation;
  62 
  63     public AppWrapper(Path workDir, String mainClass, Source... source) {
  64         this.workDir = workDir;
  65         this.sources = Arrays.asList(source);
  66         this.mainClass = mainClass;
  67         List<String> jars = sources.stream()
  68                 .filter(src -> src.getFullName().equals(mainClass))
  69                 .map(Source::getJarName).collect(toList());
  70         if (jars.size() != 1) {
  71             throw new IllegalArgumentException(
  72                     format("Can not determine main jar : " + jars));
  73         }
  74         mainJar = jars.get(0);
  75     }
  76 
  77     public AppWrapper(Path workDir, String mainClass,
  78             String paramsForCompilation, Source... source) {
  79         this(workDir, mainClass, source);
  80         this.paramsForJarsCompilation = paramsForCompilation;
  81     }
  82 
  83     /**
  84      * copy the old AppWrapper, but change the work dir
  85      */
  86     public AppWrapper(AppWrapper copied, Path workDir) {
  87         this.workDir = workDir;
  88         sources = new ArrayList<>(copied.sources);
  89         mainJar = copied.mainJar;
  90         mainClass = copied.mainClass;
  91     }
  92 
  93     public String getAppName() {
  94         return mainClass.substring(mainClass.lastIndexOf('.') + 1);
  95     }
  96 
  97     public void preinstallApp(ExtensionType extension) throws IOException {
  98         createSrcBundleAndBinDirs();
  99 
 100         if (!getJarTempSources().isEmpty()) {
 101             Utils.createDir(getJarDir());
 102         }
 103 
 104         if (!getModuleTempSources().isEmpty()) {
 105             Utils.createDir(
 106                     Paths.get(getModulePathBasedOnExtension(extension)));
 107         }
 108     }
 109 
 110     private void createSrcBundleAndBinDirs() throws IOException {
 111         Utils.createDir(getSrcDir());
 112         Utils.createDir(getBundlesDir());
 113         Utils.createDir(getBinDir());
 114     }
 115 
 116     public void preinstallApp(ExtensionType[] extensionArray) throws IOException {
 117         createSrcBundleAndBinDirs();
 118 
 119         for (ExtensionType extension : extensionArray) {
 120             Utils.createDir(Paths.get(getModulePathBasedOnExtension(extension)));
 121         }
 122     }
 123 
 124 
 125     private List<Source> getJarTempSources() {
 126         return sources.stream().filter((source) -> !source.isModule())
 127                 .collect(Collectors.toList());
 128     }
 129 
 130     public Path getWorkDir() {
 131         return workDir;
 132     }
 133 
 134     public void setWorkDir(Path workDir) {
 135         this.workDir = workDir;
 136     }
 137 
 138     public List<Source> getSources() {
 139         return sources;
 140     }
 141 
 142     public String getMainClass() {
 143         return this.mainClass;
 144     }
 145 
 146     public Path getBundlesDir() {
 147         return getWorkDir().resolve(BUNDLES);
 148     }
 149 
 150     private Path getSrcDir() {
 151         return getWorkDir().resolve(SOURCE);
 152     }
 153 
 154     public Path getJarDir() {
 155         return getWorkDir().resolve(JARS);
 156     }
 157 
 158     private Path getBinDir() {
 159         return getWorkDir().resolve(BIN);
 160     }
 161 
 162     public Path getMainJarFile() {
 163         return getJarDir().resolve(mainJar + ".jar");
 164     }
 165 
 166     public void writeSourcesToAppDirectory() throws IOException {
 167         Path appDir = getSrcDir();
 168         for (Source source : getModuleTempSources()) {
 169             source.generateSourceForModule(appDir);
 170         }
 171 
 172         for (Source source : getJarTempSources()) {
 173             source.generateSourceForJar(appDir);
 174         }
 175     }
 176 
 177     public int compileApp(ExtensionType extension, Path... classpath)
 178             throws IOException {
 179         return compileApp(new String[0], extension, classpath);
 180     }
 181 
 182     private int compileApp(String[] javacOptions, ExtensionType extension,
 183             Path... classpath) throws IOException {
 184         int resultForModule = compileAppForModules(javacOptions, extension,
 185                 classpath);
 186         int resultForJar = compileAppForJars(javacOptions, extension,
 187                 classpath);
 188         int result = 0;
 189         if (resultForJar < 0 || resultForModule < 0) {
 190             result = -1;
 191         }
 192         return result;
 193     }
 194 
 195     public int compileApp(Path... classpath) throws IOException {
 196         return compileApp(new String[0], classpath);
 197     }
 198 
 199     public int compileApp(String[] javacOptions, Path... classpath)
 200             throws IOException {
 201         return compileApp(javacOptions, null, classpath);
 202     }
 203 
 204     public int compileAndCreateExtensionEndProduct(ExtensionType extension, Path... classpath) throws IOException, ExecutionException  {
 205         int resultForModule = compileAppForModules(new String[0], extension, classpath);
 206         jarApp(extension);
 207         compileAppForJars(new String[0], extension, classpath);
 208         jarApp(ExtensionType.NormalJar);
 209         return resultForModule;
 210     }
 211 
 212     private int compileAppForJars(String[] javacOptions,
 213             ExtensionType extension, Path[] classpath) throws IOException {
 214         if (getJarTempSources().isEmpty()) {
 215             return 0;
 216         }
 217         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 218         final List<String> argsList = new ArrayList<>();
 219 
 220         if (javacOptions != null && javacOptions.length > 0) {
 221             for (int i = 0; i < javacOptions.length; i++) {
 222                 String javacOption = javacOptions[i];
 223                 argsList.add(javacOption);
 224             }
 225         }
 226 
 227         argsList.add("-d");
 228         argsList.add(getBinDir().toString());
 229 
 230         int result = 0;
 231         for (Source tempSource : getJarTempSources()) {
 232             List<String> newArgs = new ArrayList<String>();
 233             newArgs.addAll(argsList);
 234 
 235 
 236             newArgs.add("-classpath");
 237             if (classpath.length != 0) {
 238                 newArgs.add(Stream.of(classpath)
 239                         .map(eachPath -> eachPath.toAbsolutePath().toString())
 240                         .collect(joining(File.pathSeparator)));
 241             } else {
 242                 newArgs.add(getBinDir().toString());
 243             }
 244 
 245             if (paramsForJarsCompilation != null) {
 246                 newArgs.add(this.paramsForJarsCompilation);
 247             }
 248 
 249             if (extension != null) {
 250                 newArgs.add("-mp");
 251                 newArgs.add(String.join(File.pathSeparator, getModulePathBasedOnExtension(extension), JMODS_PATH_IN_JDK));
 252             }
 253 
 254             String string = getSrcDir() + File.separator
 255                     + tempSource.getPackageName().replace(".", File.separator);
 256             Path path = Paths.get(string);
 257             Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
 258                 @Override
 259                 public FileVisitResult visitFile(Path file,
 260                         BasicFileAttributes attr) {
 261 
 262                     if (file.toString().endsWith(".java")) {
 263                         newArgs.add(file.toString());
 264                     }
 265                     return FileVisitResult.CONTINUE;
 266                 }
 267             });
 268 
 269             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 270             System.out.println(
 271                     "====================COMPILATION STARTS===========================");
 272             System.out.println("compilation command for jars is " + newArgs);
 273             result = compiler.run(System.in, outputStream, System.err,
 274                     newArgs.toArray(new String[newArgs.size()]));
 275             System.out.println(
 276                     "===================COMPILATION ENDS===============================");
 277             System.out.println();
 278         }
 279         return result;
 280     }
 281 
 282     private int compileAppForModules(String[] javacOptions,
 283             ExtensionType extension, Path... classpath) throws IOException {
 284         if (getModuleTempSources().isEmpty()) {
 285             return 0;
 286         }
 287         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 288         int result = 0;
 289         for (Source source : getModuleTempSources()) {
 290             final List<String> argsList = new ArrayList<>();
 291 
 292             if (javacOptions != null && javacOptions.length > 0) {
 293                 for (int i = 0; i < javacOptions.length; i++) {
 294                     String javacOption = javacOptions[i];
 295                     argsList.add(javacOption);
 296                 }
 297             }
 298 
 299             argsList.add("-mp");
 300             argsList.add(String.join(File.pathSeparator, getBinDir().toString(), JMODS_PATH_IN_JDK));
 301             argsList.add("-d");
 302             argsList.add(String.join(File.separator, getBinDir().toString(),
 303                     source.getModuleName()));
 304             Files.walkFileTree(
 305                     Paths.get(String.join(File.separator,
 306                             getSrcDir().toString(), source.getModuleName())),
 307                     new SimpleFileVisitor<Path>() {
 308                         public FileVisitResult visitFile(Path file,
 309                                 BasicFileAttributes attr) {
 310                             if (file.toString().endsWith(".java")) {
 311                                 argsList.add(file.toString());
 312                             }
 313                             return FileVisitResult.CONTINUE;
 314                         }
 315                     });
 316             if (classpath.length != 0) {
 317                 argsList.add("-classpath");
 318                 argsList.add(Stream.of(classpath)
 319                         .map(path -> path.toAbsolutePath().toString())
 320                         .collect(joining(File.pathSeparator)));
 321             }
 322             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 323             System.out.println(
 324                     "====================COMPILATION STARTS===========================");
 325             System.out.println(
 326                     "compilation command for modules is " + argsList);
 327 
 328             int tempResult = compiler.run(System.in, outputStream, System.err,
 329                     argsList.toArray(new String[argsList.size()]));
 330             if (tempResult != 0) {
 331                 result = tempResult;
 332             }
 333             String out = outputStream.toString();
 334             if (!out.trim().isEmpty()) {
 335                 LOG.log(Level.INFO, out);
 336             }
 337             System.out.println(
 338                     "===================COMPILATION ENDS===============================");
 339             System.out.println();
 340         }
 341         return result;
 342     }
 343 
 344     public List<Path> getJarFilesList() throws IOException {
 345         try (DirectoryStream<Path> jarsStream = Files
 346                 .newDirectoryStream(getJarDir(), "*.jar")) {
 347             List<Path> jars = new ArrayList<>();
 348             jarsStream.forEach(jars::add);
 349             return jars;
 350         }
 351     }
 352 
 353     private void createSimpleJar(List<Pair<String, String>> services,
 354             boolean crossClassPath) throws IOException {
 355 
 356         if (getJarTempSources().isEmpty()) {
 357             return;
 358         }
 359         Map<String, List<Source>> jars = getJarTempSources().stream()
 360                 .collect(Collectors.groupingBy(Source::getJarName));
 361 
 362         for (Entry<String, List<Source>> entry : jars.entrySet()) {
 363             String jarFileName = entry.getKey();
 364             Path jarFile = getJarDir().resolve(jarFileName + ".jar");
 365             Manifest manifest = new Manifest();
 366             manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION,
 367                     "1.0");
 368             if (crossClassPath)
 369                 manifest.getMainAttributes().put(Attributes.Name.CLASS_PATH,
 370                         jars.keySet().stream()
 371                                 .filter(str -> !str.equals(jarFileName))
 372                                 .map(str -> str + ".jar")
 373                                 .collect(joining(File.pathSeparator)));
 374             if (mainJar.equals(jarFileName)) {
 375                 manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS,
 376                         mainClass);
 377             }
 378 
 379             try (JarOutputStream jarOutputStream = new JarOutputStream(
 380                     Files.newOutputStream(jarFile), manifest)) {
 381                 add(jarOutputStream, new HashSet<>(entry.getValue()));
 382                 for (Pair<String, String> service : services) {
 383                     if (!entry.getValue().stream().anyMatch(source -> source
 384                             .getFullName().equals(service.getValue()))) {
 385                         continue;
 386                     }
 387                     jarOutputStream.putNextEntry(new JarEntry(
 388                             "META-INF/services/" + service.getKey()));
 389                     jarOutputStream.write(service.getValue().getBytes());
 390                     jarOutputStream.closeEntry();
 391                 }
 392             }
 393         }
 394     }
 395 
 396     private void add(JarOutputStream jarStream, Set<Source> sources)
 397             throws IOException {
 398         Path classesDir = getBinDir();
 399         for (Source src : sources) {
 400             Path packageDir = classesDir
 401                     .resolve(src.getPackageName().replace('.', '/'));
 402             // we need to process inner classes like App1$1
 403             DirectoryStream<Path> classFiles = newDirectoryStream(packageDir,
 404                     src.getSimpleName() + "*.class");
 405             for (Path classFile : classFiles) {
 406                 ZipEntry entry = new JarEntry(
 407                         src.getPackageName().replace('.', '/') + "/"
 408                                 + classFile.getFileName().toString());
 409                 jarStream.putNextEntry(entry);
 410                 jarStream.write(Files.readAllBytes(classFile));
 411                 jarStream.closeEntry();
 412             }
 413         }
 414     }
 415 
 416     public List<Source> getModuleTempSources() {
 417         return sources.stream().filter((source) -> source.isModule())
 418                 .collect(Collectors.toList());
 419     }
 420 
 421     public Path getExplodedModsDir() {
 422         return getBinDir();
 423     }
 424 
 425     public Path getJmodsDir() {
 426         return getWorkDir().resolve(JMODS_DIR);
 427     }
 428 
 429     public Path getModularJarsDir() {
 430         return getWorkDir().resolve(MODULAR_JARS_DIR);
 431     }
 432 
 433     public List<Path> getModularJarFileList() throws IOException {
 434         try (DirectoryStream<Path> jarsStream = Files
 435                 .newDirectoryStream(getModularJarsDir(), "*.jar")) {
 436             List<Path> jars = new ArrayList<>();
 437             jarsStream.forEach(jars::add);
 438             return jars;
 439         }
 440     }
 441 
 442     public List<Path> getJmodFileList() throws IOException {
 443         try (DirectoryStream<Path> jarsStream = Files
 444                 .newDirectoryStream(getJmodsDir(), "*.jmod")) {
 445             List<Path> jmods = new ArrayList<>();
 446             jarsStream.forEach(jmods::add);
 447             return jmods;
 448         }
 449     }
 450 
 451     /*
 452      * list mod directory
 453      */
 454 
 455     public List<Path> getExplodedModFileList() throws IOException {
 456         try (DirectoryStream<Path> jarsStream = Files
 457                 .newDirectoryStream(getExplodedModsDir(), "*")) {
 458             List<Path> modFiles = new ArrayList<>();
 459             jarsStream.forEach(modFiles::add);
 460             return modFiles;
 461         }
 462     }
 463 
 464     public void jarApp(ExtensionType extension)
 465             throws IOException, ExecutionException {
 466         switch (extension) {
 467         case NormalJar:
 468             createSimpleJar(Collections.emptyList(), false);
 469             break;
 470         case ModularJar:
 471             createModularJar(Collections.emptyList(), false);
 472             break;
 473         case ExplodedModules: /******************************************
 474                                * bin directory itself is exploded modules
 475                                * directory
 476                                ******************************************/
 477             break;
 478         case Jmods:
 479             createJmod();
 480             break;
 481         }
 482     }
 483 
 484     public void jarApp(List<Pair<String, String>> services,
 485             boolean crossClassPath) throws IOException, ExecutionException {
 486         createSimpleJar(services, crossClassPath);
 487         createModularJar(services, crossClassPath);
 488     }
 489 
 490     private void createJmod() throws IOException, ExecutionException {
 491         for (Source source : getModuleTempSources()) {
 492             List<String> command = new ArrayList<String>();
 493             command.add("jmod");
 494             command.add("create");
 495             command.add("--class-path");
 496             command.add(getBinDir().toString() + File.separator
 497                     + source.getModuleName());
 498             command.add("--main-class");
 499             command.add(source.getFullName());
 500             command.add(getJmodsDir() + File.separator + source.getModuleName()
 501                     + ".jmod");
 502             System.out.println(
 503                     "=========================JMOD CREATION STARTS=========================");
 504             Utils.runCommand(command, CONFIG_INSTANCE.getInstallTimeout());
 505             System.out.println(
 506                     "=========================JMOD CREATION ENDS===========================");
 507             System.out.println();
 508         }
 509     }
 510 
 511     private void createModularJar(List<Pair<String, String>> services,
 512             boolean crossClassPath) throws IOException, ExecutionException {
 513 
 514         if (getModuleTempSources().isEmpty()) {
 515             return;
 516         }
 517         /*
 518          * @TODO check whether services are required in modular jar Need to
 519          * change below code Implementation pending. complete this
 520          * implementation.
 521          */
 522         if (!services.isEmpty()) {
 523             Map<String, List<Source>> jars = sources.stream()
 524                     .collect(Collectors.groupingBy(Source::getJarName));
 525 
 526             // for (Map.Entry<String, List<TempSource>> entry : jars.entrySet())
 527             // {
 528             // String jarFileName = entry.getKey();
 529             //// Path jarFile = getJarDir().resolve(jarFileName + ".jar");
 530             // Manifest manifest = new Manifest();
 531             // manifest.getMainAttributes()
 532             // .put(Attributes.Name.MANIFEST_VERSION, "1.0");
 533             // if (crossClassPath) {
 534             // manifest.getMainAttributes()
 535             // .put(Attributes.Name.CLASS_PATH,
 536             // jars.keySet().stream()
 537             // .filter(str -> !str
 538             // .equals(jarFileName))
 539             // .map(str -> str + ".jar")
 540             // .collect(joining(File.pathSeparator)));
 541             // }
 542             // if (mainJar != null && jarFileName != null &&
 543             // mainJar.equals(jarFileName)) {
 544             // manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS,
 545             // mainClass);
 546             // }
 547             // }
 548             /*
 549              * Dummy code
 550              *
 551              * @TODO complete implementation.
 552              */
 553 
 554             for (Source tempSource : getModuleTempSources()) {
 555                 Manifest manifest = new Manifest();
 556                 String jarFileName = tempSource.getJarName();
 557                 manifest.getMainAttributes()
 558                         .put(Attributes.Name.MANIFEST_VERSION, "1.0");
 559                 if (crossClassPath) {
 560                     manifest.getMainAttributes()
 561                             .put(Attributes.Name.CLASS_PATH,
 562                                     jars.keySet().stream()
 563                                             .filter(str -> !str
 564                                                     .equals(jarFileName))
 565                                     .map(str -> str + ".jar")
 566                                     .collect(joining(File.pathSeparator)));
 567                 }
 568                 if (mainJar != null && jarFileName != null
 569                         && mainJar.equals(jarFileName)) {
 570                     manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS,
 571                             mainClass);
 572                 }
 573 
 574                 // try (JarOutputStream jarOutputStream = new JarOutputStream(
 575                 // Files.newOutputStream(Paths.get(jarFileName)), manifest)) {
 576                 // Stream<Path> walk =
 577                 // Files.walk(Paths.get(getModularJarsDir().toString() +
 578                 // File.separatorChar + tempSource.getModuleName()),
 579                 // Integer.MAX_VALUE);
 580                 // String join = String.join(File.separator,
 581                 // getModularJarsDir().toString(), tempSource.getModuleName(),
 582                 // tempSource.getModuleName().replace('.', File.separatorChar));
 583                 // Path moduleInfoPath =
 584                 // getModularJarsDir().resolve(tempSource.getModuleName().replace('.',
 585                 // '/')).resolve("module-info.class");
 586                 // ZipEntry zipEntry = new JarEntry(
 587                 // moduleInfoPath.getFileName().toString());
 588                 // jarOutputStream.putNextEntry(zipEntry);
 589                 // jarOutputStream.write(Files.readAllBytes(moduleInfoPath));
 590                 // jarOutputStream.closeEntry();
 591                 //
 592                 // String packageName =
 593                 // getModularJarsDir().toString().replace('.', '/');
 594                 // DirectoryStream<Path> classFiles = newDirectoryStream(
 595                 // getModularJarsDir().resolve(packageName), "*.class");
 596                 // for (Path eachClassFile : classFiles) {
 597                 // ZipEntry jarEntry = new JarEntry("com/greetings" + "/"
 598                 // + eachClassFile.getFileName().toString());
 599                 // jarOutputStream.putNextEntry(jarEntry);
 600                 // jarOutputStream.write(Files.readAllBytes(eachClassFile));
 601                 // jarOutputStream.closeEntry();
 602                 // }
 603                 // }
 604 
 605                 // Files.walkFileTree(Paths.get(getModsDir().toString() +
 606                 // File.separatorChar + tempSource.getModuleName()), new
 607                 // SimpleFileVisitor<Path>() {
 608                 // @Override
 609                 // public FileVisitResult visitFile(Path file,
 610                 // BasicFileAttributes attr) {
 611                 //
 612                 // if ("module-info.java".equals(file.toString())) {
 613                 // ZipEntry zipEntry = new JarEntry(
 614                 // moduleInfoPath.getFileName().toString());
 615                 // jarOutputStream.putNextEntry(zipEntry);
 616                 // jarOutputStream.write(Files.readAllBytes(moduleInfoPath));
 617                 // jarOutputStream.closeEntry();
 618                 // }
 619                 // return FileVisitResult.CONTINUE;
 620                 // }
 621                 // });
 622 
 623             }
 624 
 625         } else {
 626             /*
 627              * JDK 9 jar creation procedure. $ jar --create
 628              * --file=mlib/org.astro@1.0.jar --module-version=1.0 -C
 629              * mods/org.astro .
 630              */
 631             for (Source source : getModuleTempSources()) {
 632                 List<String> command = new ArrayList<String>();
 633                 command.add("jar");
 634                 command.add("--create");
 635                 command.add("--file=" + getModularJarsDir() + File.separator
 636                         + source.getJarName() + ".jar");
 637                 command.add("--module-version=1.0");
 638                 if (source.getFullName() != null && this.mainClass != null
 639                         && source.getFullName().equals(this.mainClass)) {
 640                     command.add("--main-class=" + this.mainClass);
 641                 }
 642                 command.add("-C");
 643                 String moduleAbsouleDirectoryPath = String.join(File.separator,
 644                         getBinDir().toString(), source.getModuleName());
 645                 command.add(moduleAbsouleDirectoryPath);
 646                 command.add(".");
 647                 System.out.println(
 648                         "====================MODULAR JAR CREATION STARTS==================");
 649                 Utils.runCommand(command, CONFIG_INSTANCE.getInstallTimeout());
 650                 System.out.println(
 651                         "====================MODULAR JAR CREATION ENDS====================");
 652                 System.out.println();
 653             }
 654         }
 655     }
 656 
 657     public String getIdentifier() {
 658         try (JarFile mainJar = new JarFile(getMainJarFile().toFile());) {
 659             Manifest manifest = mainJar.getManifest();
 660             for (Map.Entry<Object, Object> entry : manifest.getMainAttributes()
 661                     .entrySet()) {
 662                 if (entry.getKey().toString().equals("Main-Class")) {
 663                     return entry.getValue().toString();
 664                 }
 665             }
 666         } catch (IOException ex) {
 667             throw new RuntimeException(ex);
 668         }
 669         return "";
 670     }
 671 
 672     public String addAllModules() {
 673         return getModuleTempSources().stream().map(Source::getModuleName)
 674                 .collect(Collectors.joining(File.pathSeparator));
 675     }
 676 
 677     public List<String> getAllModuleNamesAsList() {
 678         return getModuleTempSources().stream().map(Source::getModuleName)
 679                 .collect(Collectors.toList());
 680     }
 681 
 682     public String getMainModuleName() {
 683         List<Source> collect = sources.stream()
 684                 .filter((source) -> source.isMainModule())
 685                 .collect(Collectors.toList());
 686         return !collect.isEmpty() ? collect.get(0).getModuleName() : null;
 687     }
 688 
 689     public boolean isAppContainsModules() {
 690         return !getModuleTempSources().isEmpty();
 691     }
 692 
 693     private String getModulePathBasedOnExtension(ExtensionType extension) {
 694         if (extension == null) {
 695             throw new NullPointerException("Extension cannot be null");
 696         }
 697         switch (extension) {
 698         case ModularJar:
 699             return getModularJarsDir().toString();
 700         case ExplodedModules:
 701             return getExplodedModsDir().toString();
 702         case Jmods:
 703             return getJmodsDir().toString();
 704         default:
 705             return getJarDir().toString();
 706         }
 707     }
 708 }