1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.File;
  25 import java.io.IOException;
  26 import java.io.OutputStream;
  27 import java.io.UnsupportedEncodingException;
  28 import java.nio.file.Files;
  29 import java.nio.file.Path;
  30 import java.nio.file.Paths;
  31 import java.util.ArrayList;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.jar.JarEntry;
  35 import java.util.jar.JarOutputStream;
  36 import java.lang.module.ModuleDescriptor;
  37 import static java.lang.module.ModuleDescriptor.Builder;
  38 
  39 /**
  40  * Jigsaw utility methods are part of this class. It exposes methods to
  41  * generate module descriptor, compile jigsaw modules, creates modular and
  42  * regular jar files and generate service descriptor inside
  43  * META-INF folder etc.
  44  */
  45 public class JigsawCommon {
  46 
  47     /**
  48      * Enum represents all supported module types in JDK9.
  49      */
  50     public enum MODULE_TYPE {
  51 
  52         STRICT, AUTO, UN_NAMED;
  53     }
  54 
  55     public static final String SPACE = " ";
  56     public static final String MODULE_INFO_DESCRIPTOR = "module-info.class";
  57 
  58     /**
  59      * Constructs a Java Command line string based on modular structure
  60      * followed by modular client and service.
  61      */
  62     public static String[] getJavaCommand(MODULE_TYPE clientModuleType,
  63             MODULE_TYPE serviceModuletype, Path clientJarFilePath,
  64             Path serviceJarFilePath, String clientModuleName,
  65             String mainClass, Map<String, String> vmArgs) throws IOException {
  66 
  67         final StringBuilder command = new StringBuilder();
  68         vmArgs.forEach((key, value) -> command.append(key + value + SPACE));
  69         if (serviceModuletype != MODULE_TYPE.UN_NAMED
  70                 && clientModuleType != MODULE_TYPE.UN_NAMED) {
  71             command.append(SPACE + "-mp" + SPACE);
  72             command.append(clientJarFilePath.getParent().toRealPath());
  73             command.append(SPACE + ("-m" + SPACE + clientModuleName + "/")
  74                     + mainClass);
  75         } else if (serviceModuletype == MODULE_TYPE.UN_NAMED
  76                 && clientModuleType == MODULE_TYPE.UN_NAMED) {
  77             command.append(SPACE + "-cp" + SPACE);
  78             command.append(clientJarFilePath.toRealPath());
  79             command.append(File.pathSeparator);
  80             command.append(serviceJarFilePath.toRealPath());
  81             command.append(SPACE + mainClass);
  82         } else {
  83             if (serviceModuletype != MODULE_TYPE.UN_NAMED) {
  84                 command.append(SPACE + "-mp" + SPACE);
  85                 command.append(serviceJarFilePath.getParent().toRealPath());
  86             } else {
  87                 command.append(SPACE + "-cp" + SPACE);
  88                 command.append(serviceJarFilePath.toRealPath());
  89             }
  90             if (clientModuleType != MODULE_TYPE.UN_NAMED) {
  91                 command.append(SPACE + "-mp" + SPACE);
  92                 command.append(clientJarFilePath.getParent().toRealPath());
  93                 command.append(SPACE + ("-m" + SPACE + clientModuleName + "/")
  94                         + mainClass);
  95             } else {
  96                 command.append(SPACE + "-cp" + SPACE);
  97                 command.append(clientJarFilePath.toRealPath());
  98                 command.append(SPACE + mainClass);
  99             }
 100         }
 101 
 102         return command.toString().trim().split("[\\s]+");
 103     }
 104 
 105     /**
 106      * Compile the source path and generates a regular/modular jar as per need.
 107      */
 108     public static boolean compileAndCreateJar(Path codePath, Path compilePath,
 109             Path jarFile, ModuleDescriptor moduleDescriptor,
 110             boolean addMetaInfServiceDescriptor,
 111             String serviceImpl, Path metaServiceDescriptor) throws IOException {
 112 
 113         System.out.println(String.format(
 114                 "Compiling source code '%s'", codePath));
 115         boolean done = CompilerUtils.compile(codePath, compilePath);
 116         System.out.println(String.format(
 117                 "Compilation completed successfully? %s", done));
 118         if (done) {
 119             if (addMetaInfServiceDescriptor) {
 120                 createMetaInfServiceDescriptor(compilePath, serviceImpl,
 121                         metaServiceDescriptor);
 122             } else {
 123                 System.out.println(
 124                         "META-INF service descriptor will not be created");
 125             }
 126 
 127             if (moduleDescriptor != null) {
 128                 System.out.println(String.format(
 129                         "Creating Modular jar file '%s'", jarFile));
 130                 createModularjar(jarFile, compilePath,
 131                         moduleDescriptor, Paths.get("."));
 132             } else {
 133                 System.out.println(String.format(
 134                         "Creating regular jar file '%s'", jarFile));
 135                 JarUtils.createJarFile(jarFile, compilePath);
 136             }
 137         }
 138 
 139         return done;
 140     }
 141 
 142     /**
 143      * Generates a modular jar with module descriptor.
 144      */
 145     public static void createModularjar(Path jarfile, Path compilePath,
 146             ModuleDescriptor moduleDescriptor, Path... file)
 147             throws IOException {
 148 
 149         Path parent = jarfile.getParent();
 150         if (parent != null) {
 151             Files.createDirectories(parent);
 152         }
 153 
 154         List<Path> entries = new ArrayList<>();
 155         for (Path entry : file) {
 156             Files.find(compilePath.resolve(entry), Integer.MAX_VALUE,
 157                     (p, attrs) -> attrs.isRegularFile())
 158                     .map(e -> compilePath.relativize(e))
 159                     .forEach(entries::add);
 160         }
 161         try (JarOutputStream jos = new JarOutputStream(
 162                 Files.newOutputStream(jarfile))) {
 163             JarEntry je = new JarEntry(MODULE_INFO_DESCRIPTOR);
 164             jos.putNextEntry(je);
 165             jdk.internal.module.ModuleInfoWriter.write(moduleDescriptor, jos);
 166             jos.closeEntry();
 167 
 168             for (Path entry : entries) {
 169                 Path normalized = entry.normalize();
 170                 String name = normalized
 171                         .subpath(0, normalized.getNameCount())
 172                         .toString()
 173                         .replace(File.separatorChar, '/');
 174 
 175                 jos.putNextEntry(new JarEntry(name));
 176                 Files.copy(compilePath.resolve(entry), jos);
 177             }
 178         }
 179     }
 180 
 181     /**
 182      * Generates a module descriptor for modular service component.
 183      */
 184     public static ModuleDescriptor generateServiceModuleDescriptor(
 185             MODULE_TYPE serviceModuletype, String moduleName,
 186             String servicePkg, String serviceInterface,
 187             String serviceImpl, List<String> requiredModules) {
 188 
 189         final Builder builder;
 190         if (serviceModuletype == MODULE_TYPE.STRICT) {
 191             System.out.println("Generating ServiceModuleDescriptor object");
 192             builder = new Builder(moduleName)
 193                     .exports(servicePkg)
 194                     .provides(serviceInterface, serviceImpl);
 195         } else {
 196             System.out.println("ServiceModuleDescriptor object not required.");
 197             return null;
 198         }
 199         requiredModules.stream().forEach(
 200                 reqMod -> builder.requires(reqMod));
 201 
 202         return builder.build();
 203     }
 204 
 205     /**
 206      * Generates a module descriptor for modular client component.
 207      */
 208     public static ModuleDescriptor generateClientModuleDescriptor(
 209             MODULE_TYPE clientModuleType, MODULE_TYPE serviceModuletype,
 210             String moduleName, String clientPkg, String serviceInterface,
 211             String serviceModuleName, List<String> requiredModules) {
 212 
 213         final Builder builder;
 214         if (clientModuleType == MODULE_TYPE.STRICT) {
 215             System.out.println("Generating ClientModuleDescriptor object");
 216             builder = new Builder(moduleName)
 217                     .exports(clientPkg)
 218                     .uses(serviceInterface);
 219             if (serviceModuletype == MODULE_TYPE.STRICT) {
 220                 builder.requires(serviceModuleName);
 221             }
 222         } else {
 223             System.out.println("ClientModuleDescriptor object not required.");
 224             return null;
 225         }
 226         requiredModules.stream().forEach(
 227                 reqMod -> builder.requires(reqMod));
 228 
 229         return builder.build();
 230     }
 231 
 232     /**
 233      * Generates service descriptor inside META-INF folder.
 234      */
 235     public static void createMetaInfServiceDescriptor(Path serviceCompilePath,
 236             String serviceImpl, Path metaServiceDescriptor)
 237             throws UnsupportedEncodingException, IOException {
 238 
 239         System.out.println(String.format("Creating META-INF service descriptor"
 240                 + " for '%s' at path '%s'", serviceImpl, serviceCompilePath));
 241         Path serviceDescriptorFilePath
 242                 = serviceCompilePath.resolve(metaServiceDescriptor);
 243         Path parent = serviceDescriptorFilePath.getParent();
 244         if (parent != null) {
 245             Files.createDirectories(parent);
 246         }
 247         Files.write(serviceDescriptorFilePath, serviceImpl.getBytes("UTF-8"));
 248         System.out.println(String.format(
 249                 "META-INF service descriptor created successfully"));
 250     }
 251 
 252 }