113 public Set<String> getModules() { 114 return modules; 115 } 116 117 @Override 118 public List<String> getExecutionArgs() { 119 return args; 120 } 121 122 @Override 123 public void storeLaunchArgs(List<String> args) { 124 try { 125 patchScripts(this, args); 126 } catch (IOException ex) { 127 throw new UncheckedIOException(ex); 128 } 129 } 130 } 131 132 private final Path root; 133 private final Path mdir; 134 private final Set<String> modules = new HashSet<>(); 135 private String targetOsName; 136 137 /** 138 * Default image builder constructor. 139 * 140 * @param root The image root directory. 141 * @throws IOException 142 */ 143 public DefaultImageBuilder(Path root) throws IOException { 144 Objects.requireNonNull(root); 145 146 this.root = root; 147 this.mdir = root.resolve("lib"); 148 Files.createDirectories(mdir); 149 } 150 151 @Override 152 public void storeFiles(ResourcePool files) { 153 try { 154 // populate targetOsName field up-front because it's used elsewhere. 155 Optional<ResourcePoolModule> javaBase = files.moduleView().findModule("java.base"); 156 javaBase.ifPresent(mod -> { 157 // fill release information available from transformed "java.base" module! 158 ModuleDescriptor desc = mod.descriptor(); 159 desc.osName().ifPresent(s -> { 160 this.targetOsName = s; 161 }); 162 }); 163 164 if (this.targetOsName == null) { 165 throw new PluginException("TargetPlatform attribute is missing for java.base module"); 166 } 218 Path lib = root.resolve(LIB_DIRNAME); 219 if (Files.isDirectory(lib)) { 220 Files.find(lib, 2, (path, attrs) -> { 221 return path.getFileName().toString().equals("jspawnhelper") 222 || path.getFileName().toString().equals("jexec"); 223 }).forEach(this::setExecutable); 224 } 225 226 // read-only legal notices/license files 227 Path legal = root.resolve(LEGAL_DIRNAME); 228 if (Files.isDirectory(legal)) { 229 Files.find(legal, 2, (path, attrs) -> { 230 return attrs.isRegularFile(); 231 }).forEach(this::setReadOnly); 232 } 233 } 234 235 // If native files are stripped completely, <root>/bin dir won't exist! 236 // So, don't bother generating launcher scripts. 237 if (Files.isDirectory(bin)) { 238 prepareApplicationFiles(files, modules); 239 } 240 } catch (IOException ex) { 241 throw new PluginException(ex); 242 } 243 } 244 245 /** 246 * Generates launcher scripts. 247 * 248 * @param imageContent The image content. 249 * @param modules The set of modules that the runtime image contains. 250 * @throws IOException 251 */ 252 protected void prepareApplicationFiles(ResourcePool imageContent, Set<String> modules) throws IOException { 253 // generate launch scripts for the modules with a main class 254 for (String module : modules) { 255 String path = "/" + module + "/module-info.class"; 256 Optional<ResourcePoolEntry> res = imageContent.findEntry(path); 257 if (!res.isPresent()) { 258 throw new IOException("module-info.class not found for " + module + " module"); 259 } 260 Optional<String> mainClass; 261 ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes()); 262 mainClass = ModuleDescriptor.read(stream).mainClass(); 263 if (mainClass.isPresent()) { 264 Path cmd = root.resolve("bin").resolve(module); 265 // generate shell script for Unix platforms 266 StringBuilder sb = new StringBuilder(); 267 sb.append("#!/bin/sh") 268 .append("\n"); 269 sb.append("JLINK_VM_OPTIONS=") 270 .append("\n"); 271 sb.append("DIR=`dirname $0`") 272 .append("\n"); 273 sb.append("$DIR/java $JLINK_VM_OPTIONS -m ") 274 .append(module).append('/') 275 .append(mainClass.get()) 276 .append(" $@\n"); 277 278 try (BufferedWriter writer = Files.newBufferedWriter(cmd, 279 StandardCharsets.ISO_8859_1, 280 StandardOpenOption.CREATE_NEW)) { 281 writer.write(sb.toString()); 282 } 283 if (root.resolve("bin").getFileSystem() 284 .supportedFileAttributeViews().contains("posix")) { 285 setExecutable(cmd); 286 } 287 // generate .bat file for Windows 288 if (isWindows()) { 289 Path bat = root.resolve(BIN_DIRNAME).resolve(module + ".bat"); 290 sb = new StringBuilder(); 291 sb.append("@echo off") 292 .append("\r\n"); 293 sb.append("set JLINK_VM_OPTIONS=") 294 .append("\r\n"); 295 sb.append("set DIR=%~dp0") 296 .append("\r\n"); 297 sb.append("\"%DIR%\\java\" %JLINK_VM_OPTIONS% -m ") 298 .append(module).append('/') 299 .append(mainClass.get()) 300 .append(" %*\r\n"); 301 302 try (BufferedWriter writer = Files.newBufferedWriter(bat, 303 StandardCharsets.ISO_8859_1, 304 StandardOpenOption.CREATE_NEW)) { 305 writer.write(sb.toString()); 306 } 307 } 308 } 309 } 310 } 311 312 @Override 313 public DataOutputStream getJImageOutputStream() { 314 try { 315 Path jimageFile = mdir.resolve(BasicImageWriter.MODULES_IMAGE_NAME); 316 OutputStream fos = Files.newOutputStream(jimageFile); 317 BufferedOutputStream bos = new BufferedOutputStream(fos); 318 return new DataOutputStream(bos); 319 } catch (IOException ex) { 320 throw new UncheckedIOException(ex); 321 } 322 } 323 324 /** 325 * Returns the file name of this entry 326 */ 327 private String entryToFileName(ResourcePoolEntry entry) { | 113 public Set<String> getModules() { 114 return modules; 115 } 116 117 @Override 118 public List<String> getExecutionArgs() { 119 return args; 120 } 121 122 @Override 123 public void storeLaunchArgs(List<String> args) { 124 try { 125 patchScripts(this, args); 126 } catch (IOException ex) { 127 throw new UncheckedIOException(ex); 128 } 129 } 130 } 131 132 private final Path root; 133 private final Map<String, String> launchers; 134 private final Path mdir; 135 private final Set<String> modules = new HashSet<>(); 136 private String targetOsName; 137 138 /** 139 * Default image builder constructor. 140 * 141 * @param root The image root directory. 142 * @throws IOException 143 */ 144 public DefaultImageBuilder(Path root, Map<String, String> launchers) throws IOException { 145 this.root = Objects.requireNonNull(root); 146 this.launchers = Objects.requireNonNull(launchers); 147 this.mdir = root.resolve("lib"); 148 Files.createDirectories(mdir); 149 } 150 151 @Override 152 public void storeFiles(ResourcePool files) { 153 try { 154 // populate targetOsName field up-front because it's used elsewhere. 155 Optional<ResourcePoolModule> javaBase = files.moduleView().findModule("java.base"); 156 javaBase.ifPresent(mod -> { 157 // fill release information available from transformed "java.base" module! 158 ModuleDescriptor desc = mod.descriptor(); 159 desc.osName().ifPresent(s -> { 160 this.targetOsName = s; 161 }); 162 }); 163 164 if (this.targetOsName == null) { 165 throw new PluginException("TargetPlatform attribute is missing for java.base module"); 166 } 218 Path lib = root.resolve(LIB_DIRNAME); 219 if (Files.isDirectory(lib)) { 220 Files.find(lib, 2, (path, attrs) -> { 221 return path.getFileName().toString().equals("jspawnhelper") 222 || path.getFileName().toString().equals("jexec"); 223 }).forEach(this::setExecutable); 224 } 225 226 // read-only legal notices/license files 227 Path legal = root.resolve(LEGAL_DIRNAME); 228 if (Files.isDirectory(legal)) { 229 Files.find(legal, 2, (path, attrs) -> { 230 return attrs.isRegularFile(); 231 }).forEach(this::setReadOnly); 232 } 233 } 234 235 // If native files are stripped completely, <root>/bin dir won't exist! 236 // So, don't bother generating launcher scripts. 237 if (Files.isDirectory(bin)) { 238 prepareApplicationFiles(files); 239 } 240 } catch (IOException ex) { 241 throw new PluginException(ex); 242 } 243 } 244 245 /** 246 * Generates launcher scripts. 247 * 248 * @param imageContent The image content. 249 * @throws IOException 250 */ 251 protected void prepareApplicationFiles(ResourcePool imageContent) throws IOException { 252 // generate launch scripts for the modules with a main class 253 for (Map.Entry<String, String> entry : launchers.entrySet()) { 254 String launcherEntry = entry.getValue(); 255 int slashIdx = launcherEntry.indexOf("/"); 256 String module, mainClassName; 257 if (slashIdx == -1) { 258 module = launcherEntry; 259 mainClassName = null; 260 } else { 261 module = launcherEntry.substring(0, slashIdx); 262 assert !module.isEmpty(); 263 mainClassName = launcherEntry.substring(slashIdx + 1); 264 assert !mainClassName.isEmpty(); 265 } 266 267 String path = "/" + module + "/module-info.class"; 268 Optional<ResourcePoolEntry> res = imageContent.findEntry(path); 269 if (!res.isPresent()) { 270 throw new IOException("module-info.class not found for " + module + " module"); 271 } 272 ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes()); 273 Optional<String> mainClass = ModuleDescriptor.read(stream).mainClass(); 274 if (mainClassName == null && mainClass.isPresent()) { 275 mainClassName = mainClass.get(); 276 } 277 278 if (mainClassName != null) { 279 // make sure main class exists! 280 if (!imageContent.findEntry("/" + module + "/" + 281 mainClassName.replace('.', '/') + ".class").isPresent()) { 282 throw new RuntimeException(module + " does not have main class: " + mainClassName); 283 } 284 285 String launcherFile = entry.getKey(); 286 Path cmd = root.resolve("bin").resolve(launcherFile); 287 // generate shell script for Unix platforms 288 StringBuilder sb = new StringBuilder(); 289 sb.append("#!/bin/sh") 290 .append("\n"); 291 sb.append("JLINK_VM_OPTIONS=") 292 .append("\n"); 293 sb.append("DIR=`dirname $0`") 294 .append("\n"); 295 sb.append("$DIR/java $JLINK_VM_OPTIONS -m ") 296 .append(module).append('/') 297 .append(mainClassName) 298 .append(" $@\n"); 299 300 try (BufferedWriter writer = Files.newBufferedWriter(cmd, 301 StandardCharsets.ISO_8859_1, 302 StandardOpenOption.CREATE_NEW)) { 303 writer.write(sb.toString()); 304 } 305 if (root.resolve("bin").getFileSystem() 306 .supportedFileAttributeViews().contains("posix")) { 307 setExecutable(cmd); 308 } 309 // generate .bat file for Windows 310 if (isWindows()) { 311 Path bat = root.resolve(BIN_DIRNAME).resolve(launcherFile + ".bat"); 312 sb = new StringBuilder(); 313 sb.append("@echo off") 314 .append("\r\n"); 315 sb.append("set JLINK_VM_OPTIONS=") 316 .append("\r\n"); 317 sb.append("set DIR=%~dp0") 318 .append("\r\n"); 319 sb.append("\"%DIR%\\java\" %JLINK_VM_OPTIONS% -m ") 320 .append(module).append('/') 321 .append(mainClassName) 322 .append(" %*\r\n"); 323 324 try (BufferedWriter writer = Files.newBufferedWriter(bat, 325 StandardCharsets.ISO_8859_1, 326 StandardOpenOption.CREATE_NEW)) { 327 writer.write(sb.toString()); 328 } 329 } 330 } else { 331 throw new IllegalArgumentException(module + " doesn't contain main class & main not specified in command line"); 332 } 333 } 334 } 335 336 @Override 337 public DataOutputStream getJImageOutputStream() { 338 try { 339 Path jimageFile = mdir.resolve(BasicImageWriter.MODULES_IMAGE_NAME); 340 OutputStream fos = Files.newOutputStream(jimageFile); 341 BufferedOutputStream bos = new BufferedOutputStream(fos); 342 return new DataOutputStream(bos); 343 } catch (IOException ex) { 344 throw new UncheckedIOException(ex); 345 } 346 } 347 348 /** 349 * Returns the file name of this entry 350 */ 351 private String entryToFileName(ResourcePoolEntry entry) { |