124
125 private final Path root;
126 private final Path mdir;
127 private final Set<String> modules = new HashSet<>();
128 private String targetOsName;
129
130 /**
131 * Default image builder constructor.
132 *
133 * @param root The image root directory.
134 * @throws IOException
135 */
136 public DefaultImageBuilder(Path root) throws IOException {
137 Objects.requireNonNull(root);
138
139 this.root = root;
140 this.mdir = root.resolve("lib");
141 Files.createDirectories(mdir);
142 }
143
144 private void storeFiles(Set<String> modules, Properties release) throws IOException {
145 if (release != null) {
146 addModules(release, modules);
147 File r = new File(root.toFile(), "release");
148 try (FileOutputStream fo = new FileOutputStream(r)) {
149 release.store(fo, null);
150 }
151 }
152 }
153
154 private void addModules(Properties props, Set<String> modules) throws IOException {
155 StringBuilder builder = new StringBuilder();
156 int i = 0;
157 for (String m : modules) {
158 builder.append(m);
159 if (i < modules.size() - 1) {
160 builder.append(",");
161 }
162 i++;
163 }
164 props.setProperty("MODULES", quote(builder.toString()));
165 }
166
167 @Override
168 public void storeFiles(ResourcePool files) {
169 try {
170 // populate release properties up-front. targetOsName
171 // field is assigned from there and used elsewhere.
172 Properties release = releaseProperties(files);
173 Path bin = root.resolve("bin");
174
175 // check any duplicated resource files
176 Map<Path, Set<String>> duplicates = new HashMap<>();
177 files.entries()
178 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
179 .collect(groupingBy(this::entryToImagePath,
180 mapping(ResourcePoolEntry::moduleName, toSet())))
181 .entrySet()
182 .stream()
183 .filter(e -> e.getValue().size() > 1)
184 .forEach(e -> duplicates.put(e.getKey(), e.getValue()));
185
186 // write non-classes resource files to the image
187 files.entries()
188 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
189 .forEach(f -> {
190 try {
191 accept(f);
192 } catch (FileAlreadyExistsException e) {
193 // error for duplicated entries
194 Path path = entryToImagePath(f);
195 UncheckedIOException x =
196 new UncheckedIOException(path + " duplicated in " +
197 duplicates.get(path), e);
198 x.addSuppressed(e);
199 throw x;
200 } catch (IOException ioExp) {
201 throw new UncheckedIOException(ioExp);
202 }
203 });
204
205 files.moduleView().modules().forEach(m -> {
206 // Only add modules that contain packages
207 if (!m.packages().isEmpty()) {
208 modules.add(m.name());
209 }
210 });
211
212 storeFiles(modules, release);
213
214 if (root.getFileSystem().supportedFileAttributeViews()
215 .contains("posix")) {
216 // launchers in the bin directory need execute permission.
217 // On Windows, "bin" also subdirectories containing jvm.dll.
218 if (Files.isDirectory(bin)) {
219 Files.find(bin, 2, (path, attrs) -> {
220 return attrs.isRegularFile() && !path.toString().endsWith(".diz");
221 }).forEach(this::setExecutable);
222 }
223
224 // jspawnhelper is in lib or lib/<arch>
225 Path lib = root.resolve("lib");
226 if (Files.isDirectory(lib)) {
227 Files.find(lib, 2, (path, attrs) -> {
228 return path.getFileName().toString().equals("jspawnhelper")
229 || path.getFileName().toString().equals("jexec");
230 }).forEach(this::setExecutable);
231 }
232 }
233
234 // If native files are stripped completely, <root>/bin dir won't exist!
235 // So, don't bother generating launcher scripts.
236 if (Files.isDirectory(bin)) {
237 prepareApplicationFiles(files, modules);
238 }
239 } catch (IOException ex) {
240 throw new PluginException(ex);
241 }
242 }
243
244 // Parse version string and return a string that includes only version part
245 // leaving "pre", "build" information. See also: java.lang.Runtime.Version.
246 private static String parseVersion(String str) {
247 return Runtime.Version.parse(str).
248 version().
249 stream().
250 map(Object::toString).
251 collect(joining("."));
252 }
253
254 private static String quote(String str) {
255 return "\"" + str + "\"";
256 }
257
258 private Properties releaseProperties(ResourcePool pool) throws IOException {
259 Properties props = new Properties();
260 Optional<ResourcePoolModule> javaBase = pool.moduleView().findModule("java.base");
261 javaBase.ifPresent(mod -> {
262 // fill release information available from transformed "java.base" module!
263 ModuleDescriptor desc = mod.descriptor();
264 desc.osName().ifPresent(s -> {
265 props.setProperty("OS_NAME", quote(s));
266 this.targetOsName = s;
267 });
268 desc.osVersion().ifPresent(s -> props.setProperty("OS_VERSION", quote(s)));
269 desc.osArch().ifPresent(s -> props.setProperty("OS_ARCH", quote(s)));
270 desc.version().ifPresent(s -> props.setProperty("JAVA_VERSION",
271 quote(parseVersion(s.toString()))));
272 desc.version().ifPresent(s -> props.setProperty("JAVA_FULL_VERSION",
273 quote(s.toString())));
274 });
275
276 if (this.targetOsName == null) {
277 throw new PluginException("TargetPlatform attribute is missing for java.base module");
278 }
279
280 Optional<ResourcePoolEntry> release = pool.findEntry("/java.base/release");
281 if (release.isPresent()) {
282 try (InputStream is = release.get().content()) {
283 props.load(is);
284 }
285 }
286
287 return props;
288 }
289
290 /**
291 * Generates launcher scripts.
292 *
293 * @param imageContent The image content.
294 * @param modules The set of modules that the runtime image contains.
295 * @throws IOException
296 */
297 protected void prepareApplicationFiles(ResourcePool imageContent, Set<String> modules) throws IOException {
298 // generate launch scripts for the modules with a main class
299 for (String module : modules) {
300 String path = "/" + module + "/module-info.class";
301 Optional<ResourcePoolEntry> res = imageContent.findEntry(path);
302 if (!res.isPresent()) {
303 throw new IOException("module-info.class not found for " + module + " module");
304 }
305 Optional<String> mainClass;
306 ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes());
307 mainClass = ModuleDescriptor.read(stream).mainClass();
308 if (mainClass.isPresent()) {
309 Path cmd = root.resolve("bin").resolve(module);
544 Path binDir = root.resolve("bin");
545 if (Files.exists(binDir.resolve("java")) ||
546 Files.exists(binDir.resolve("java.exe"))) {
547 return new DefaultExecutableImage(root, retrieveModules(root));
548 }
549 return null;
550 }
551
552 private static Set<String> retrieveModules(Path root) {
553 Path releaseFile = root.resolve("release");
554 Set<String> modules = new HashSet<>();
555 if (Files.exists(releaseFile)) {
556 Properties release = new Properties();
557 try (FileInputStream fi = new FileInputStream(releaseFile.toFile())) {
558 release.load(fi);
559 } catch (IOException ex) {
560 System.err.println("Can't read release file " + ex);
561 }
562 String mods = release.getProperty("MODULES");
563 if (mods != null) {
564 String[] arr = mods.split(",");
565 for (String m : arr) {
566 modules.add(m.trim());
567 }
568
569 }
570 }
571 return modules;
572 }
573 }
|
124
125 private final Path root;
126 private final Path mdir;
127 private final Set<String> modules = new HashSet<>();
128 private String targetOsName;
129
130 /**
131 * Default image builder constructor.
132 *
133 * @param root The image root directory.
134 * @throws IOException
135 */
136 public DefaultImageBuilder(Path root) throws IOException {
137 Objects.requireNonNull(root);
138
139 this.root = root;
140 this.mdir = root.resolve("lib");
141 Files.createDirectories(mdir);
142 }
143
144 private void storeRelease(ResourcePool pool) throws IOException {
145 Properties props = new Properties();
146 Optional<ResourcePoolEntry> release = pool.findEntry("/java.base/release");
147 if (release.isPresent()) {
148 try (InputStream is = release.get().content()) {
149 props.load(is);
150 }
151 }
152 File r = new File(root.toFile(), "release");
153 try (FileOutputStream fo = new FileOutputStream(r)) {
154 props.store(fo, null);
155 }
156 }
157
158 @Override
159 public void storeFiles(ResourcePool files) {
160 try {
161 // populate targetOsName field up-front because it's used elsewhere.
162 Optional<ResourcePoolModule> javaBase = files.moduleView().findModule("java.base");
163 javaBase.ifPresent(mod -> {
164 // fill release information available from transformed "java.base" module!
165 ModuleDescriptor desc = mod.descriptor();
166 desc.osName().ifPresent(s -> {
167 this.targetOsName = s;
168 });
169 });
170
171 if (this.targetOsName == null) {
172 throw new PluginException("TargetPlatform attribute is missing for java.base module");
173 }
174
175 // store 'release' file
176 storeRelease(files);
177
178 Path bin = root.resolve("bin");
179 // check any duplicated resource files
180 Map<Path, Set<String>> duplicates = new HashMap<>();
181 files.entries()
182 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
183 .collect(groupingBy(this::entryToImagePath,
184 mapping(ResourcePoolEntry::moduleName, toSet())))
185 .entrySet()
186 .stream()
187 .filter(e -> e.getValue().size() > 1)
188 .forEach(e -> duplicates.put(e.getKey(), e.getValue()));
189
190 // write non-classes resource files to the image
191 files.entries()
192 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
193 .forEach(f -> {
194 try {
195 accept(f);
196 } catch (FileAlreadyExistsException e) {
197 // error for duplicated entries
198 Path path = entryToImagePath(f);
199 UncheckedIOException x =
200 new UncheckedIOException(path + " duplicated in " +
201 duplicates.get(path), e);
202 x.addSuppressed(e);
203 throw x;
204 } catch (IOException ioExp) {
205 throw new UncheckedIOException(ioExp);
206 }
207 });
208
209 files.moduleView().modules().forEach(m -> {
210 // Only add modules that contain packages
211 if (!m.packages().isEmpty()) {
212 modules.add(m.name());
213 }
214 });
215
216 if (root.getFileSystem().supportedFileAttributeViews()
217 .contains("posix")) {
218 // launchers in the bin directory need execute permission.
219 // On Windows, "bin" also subdirectories containing jvm.dll.
220 if (Files.isDirectory(bin)) {
221 Files.find(bin, 2, (path, attrs) -> {
222 return attrs.isRegularFile() && !path.toString().endsWith(".diz");
223 }).forEach(this::setExecutable);
224 }
225
226 // jspawnhelper is in lib or lib/<arch>
227 Path lib = root.resolve("lib");
228 if (Files.isDirectory(lib)) {
229 Files.find(lib, 2, (path, attrs) -> {
230 return path.getFileName().toString().equals("jspawnhelper")
231 || path.getFileName().toString().equals("jexec");
232 }).forEach(this::setExecutable);
233 }
234 }
235
236 // If native files are stripped completely, <root>/bin dir won't exist!
237 // So, don't bother generating launcher scripts.
238 if (Files.isDirectory(bin)) {
239 prepareApplicationFiles(files, modules);
240 }
241 } catch (IOException ex) {
242 throw new PluginException(ex);
243 }
244 }
245
246 /**
247 * Generates launcher scripts.
248 *
249 * @param imageContent The image content.
250 * @param modules The set of modules that the runtime image contains.
251 * @throws IOException
252 */
253 protected void prepareApplicationFiles(ResourcePool imageContent, Set<String> modules) throws IOException {
254 // generate launch scripts for the modules with a main class
255 for (String module : modules) {
256 String path = "/" + module + "/module-info.class";
257 Optional<ResourcePoolEntry> res = imageContent.findEntry(path);
258 if (!res.isPresent()) {
259 throw new IOException("module-info.class not found for " + module + " module");
260 }
261 Optional<String> mainClass;
262 ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes());
263 mainClass = ModuleDescriptor.read(stream).mainClass();
264 if (mainClass.isPresent()) {
265 Path cmd = root.resolve("bin").resolve(module);
500 Path binDir = root.resolve("bin");
501 if (Files.exists(binDir.resolve("java")) ||
502 Files.exists(binDir.resolve("java.exe"))) {
503 return new DefaultExecutableImage(root, retrieveModules(root));
504 }
505 return null;
506 }
507
508 private static Set<String> retrieveModules(Path root) {
509 Path releaseFile = root.resolve("release");
510 Set<String> modules = new HashSet<>();
511 if (Files.exists(releaseFile)) {
512 Properties release = new Properties();
513 try (FileInputStream fi = new FileInputStream(releaseFile.toFile())) {
514 release.load(fi);
515 } catch (IOException ex) {
516 System.err.println("Can't read release file " + ex);
517 }
518 String mods = release.getProperty("MODULES");
519 if (mods != null) {
520 String[] arr = mods.substring(1, mods.length() - 1).split(" ");
521 for (String m : arr) {
522 modules.add(m.trim());
523 }
524
525 }
526 }
527 return modules;
528 }
529 }
|