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