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 } 167 168 Path bin = root.resolve(BIN_DIRNAME); 169 170 // check any duplicated resource files 171 Map<Path, Set<String>> duplicates = new HashMap<>(); 172 files.entries() 173 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE) 174 .collect(groupingBy(this::entryToImagePath, 175 mapping(ResourcePoolEntry::moduleName, toSet()))) 176 .entrySet() 177 .stream() 178 .filter(e -> e.getValue().size() > 1) 179 .forEach(e -> duplicates.put(e.getKey(), e.getValue())); 180 181 // write non-classes resource files to the image 182 files.entries() 183 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE) 184 .forEach(f -> { 185 try { 186 accept(f); 187 } catch (FileAlreadyExistsException e) { 188 // error for duplicated entries 189 Path path = entryToImagePath(f); 190 UncheckedIOException x = 191 new UncheckedIOException(path + " duplicated in " + 192 duplicates.get(path), e); 193 x.addSuppressed(e); 194 throw x; 195 } catch (IOException ioExp) { 196 throw new UncheckedIOException(ioExp); 197 } 198 }); 199 200 files.moduleView().modules().forEach(m -> { 201 // Only add modules that contain packages 202 if (!m.packages().isEmpty()) { 203 modules.add(m.name()); 204 } 205 }); 206 207 if (root.getFileSystem().supportedFileAttributeViews() 208 .contains("posix")) { 209 // launchers in the bin directory need execute permission. 210 // On Windows, "bin" also subdirectories containing jvm.dll. 211 if (Files.isDirectory(bin)) { 212 Files.find(bin, 2, (path, attrs) -> { 213 return attrs.isRegularFile() && !path.toString().endsWith(".diz"); 214 }).forEach(this::setExecutable); 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(); | 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 } 167 168 checkResourcePool(files); 169 170 Path bin = root.resolve(BIN_DIRNAME); 171 172 // write non-classes resource files to the image 173 files.entries() 174 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE) 175 .forEach(f -> { 176 try { 177 accept(f); 178 } catch (FileAlreadyExistsException e) { 179 // Should not happen! Duplicates checking already done! 180 throw new AssertionError("Duplicate entry!", e); 181 } catch (IOException ioExp) { 182 throw new UncheckedIOException(ioExp); 183 } 184 }); 185 186 files.moduleView().modules().forEach(m -> { 187 // Only add modules that contain packages 188 if (!m.packages().isEmpty()) { 189 modules.add(m.name()); 190 } 191 }); 192 193 if (root.getFileSystem().supportedFileAttributeViews() 194 .contains("posix")) { 195 // launchers in the bin directory need execute permission. 196 // On Windows, "bin" also subdirectories containing jvm.dll. 197 if (Files.isDirectory(bin)) { 198 Files.find(bin, 2, (path, attrs) -> { 199 return attrs.isRegularFile() && !path.toString().endsWith(".diz"); 200 }).forEach(this::setExecutable); 211 212 // read-only legal notices/license files 213 Path legal = root.resolve(LEGAL_DIRNAME); 214 if (Files.isDirectory(legal)) { 215 Files.find(legal, 2, (path, attrs) -> { 216 return attrs.isRegularFile(); 217 }).forEach(this::setReadOnly); 218 } 219 } 220 221 // If native files are stripped completely, <root>/bin dir won't exist! 222 // So, don't bother generating launcher scripts. 223 if (Files.isDirectory(bin)) { 224 prepareApplicationFiles(files); 225 } 226 } catch (IOException ex) { 227 throw new PluginException(ex); 228 } 229 } 230 231 private void checkResourcePool(ResourcePool pool) { 232 // For now, only duplicate resources check. Add more checks here (if any) 233 checkDuplicateResources(pool); 234 } 235 236 private void checkDuplicateResources(ResourcePool pool) { 237 // check any duplicated resources 238 Map<Path, Set<String>> duplicates = new HashMap<>(); 239 pool.entries() 240 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE) 241 .collect(groupingBy(this::entryToImagePath, 242 mapping(ResourcePoolEntry::moduleName, toSet()))) 243 .entrySet() 244 .stream() 245 .filter(e -> e.getValue().size() > 1) 246 .forEach(e -> duplicates.put(e.getKey(), e.getValue())); 247 if (!duplicates.isEmpty()) { 248 throw new PluginException("Duplicate resources: " + duplicates); 249 } 250 } 251 252 /** 253 * Generates launcher scripts. 254 * 255 * @param imageContent The image content. 256 * @throws IOException 257 */ 258 protected void prepareApplicationFiles(ResourcePool imageContent) throws IOException { 259 // generate launch scripts for the modules with a main class 260 for (Map.Entry<String, String> entry : launchers.entrySet()) { 261 String launcherEntry = entry.getValue(); 262 int slashIdx = launcherEntry.indexOf("/"); 263 String module, mainClassName; 264 if (slashIdx == -1) { 265 module = launcherEntry; 266 mainClassName = null; 267 } else { 268 module = launcherEntry.substring(0, slashIdx); 269 assert !module.isEmpty(); 270 mainClassName = launcherEntry.substring(slashIdx + 1); 271 assert !mainClassName.isEmpty(); |