Alan, thank you very much for your help. This is what I was looking for. Only one moment - as I understand the ModuleFinder that you implemented can work only with .war modules. However, at one JPMS layer I want to place .war modules and .jar modules. What should I do in such situation? Should I do ModuleFinder commonFinder = ModuleFinder.compose(jarFinder, warFinder); Configuration cf = boot.configuration().resolve(commonFinder, ModuleFinder.of(), Set.of(jars+wars)); ?
Best regards, Pavel >Пятница, 28 сентября 2018, 16:52 +03:00 от Alan Bateman ><alan.bate...@oracle.com>: > >On 28/09/2018 13:10, Alex Sviridov wrote: >> Hi Alan >> >> Thank you for your answer. But my main problem is not jars inside .war >> - this is a so far from my current problem. Now I need to 1) add .war >> file to layer 2). to map file location, for example instead of >> "module-info.java" I must find "WEB-INF/classes/module-info.java" etc. >> That is all I need. How can I do it without implementing ModuleFinder? >You'll need a ModuleFinder because the packaging formats that >ModuleFinder.of(Path) is required to support doesn't know anything about >WAR files. It's not super difficult to develop your own. I attach a >simple implementation that may get you started. It's really basic but >would need a few iterations to be robust. Invoke >WarModuleFinder.of(Path) with the file path to the WAR file and it will >create a ModuleFinder that can find the application module in the WAR >file. A more complete implementation would be a lot more robust and >polished that this sample, it would also find the modules WEB-INF/lib. > >Once you have a ModuleFinder then you specify it to Conguration::resolve >method when resolving the application as the root module. You'll >probably start with something like: > > Path war = Path.of("app.war"); > ModuleFinder finder = WarModuleFinder.of(war); > > String appModuleName = finder.findAll().stream() > .findFirst() > .map(ModuleReference::descriptor) > .map(ModuleDescriptor::name) > .orElseThrow(); > > ModuleLayer boot = ModuleLayer.boot(); > Configuration cf = boot.configuration().resolve(finder, >ModuleFinder.of(), Set.of(appModuleName)); > ModuleLayer layer = boot.defineModulesWithOneLoader(cf, >ClassLoader.getSystemClassLoader()); > >and now you have a module layer with the application module loaded from >the WEB-INF/classes part of the WAR file. > >-Alan > > > static class WarModuleFinder implements ModuleFinder { > private final FileSystem warfs; > private final Path classes; > private final ModuleReference mref; > > private WarModuleFinder(Path warfile) throws IOException { > ClassLoader scl = ClassLoader.getSystemClassLoader(); > FileSystem fs = FileSystems.newFileSystem(warfile, scl); > Path classes = fs.getPath("/WEB-INF/classes"); > > ModuleDescriptor descriptor; > try (InputStream in = >Files.newInputStream(classes.resolve("module-info.class"))) { > descriptor = ModuleDescriptor.read(in, () -> >packages(classes)); > } > > this.warfs = fs; > this.classes = classes; > this.mref = new ModuleReference(descriptor, classes.toUri()) { > @Override > public ModuleReader open() { > return new WarModuleReader(); > } > public String toString() { > StringBuilder sb = new StringBuilder(); > sb.append("[module "); > sb.append(descriptor().name()); > sb.append(", location="); > sb.append(location()); > sb.append("]"); > return sb.toString(); > } > }; > } > > static WarModuleFinder of(Path war) throws IOException { > return new WarModuleFinder(war); > } > > @Override > public Optional<ModuleReference> find(String name) { > if (name.equals(mref.descriptor().name())) { > return Optional.of(mref); > } else { > return Optional.empty(); > } > } > > @Override > public Set<ModuleReference> findAll() { > return Set.of(mref); > } > > private Set<String> packages(Path classes) { > try { > return Files.find(classes, Integer.MAX_VALUE, > (path, attrs) -> !attrs.isDirectory()) > .map(entry -> classes.relativize(entry).toString()) > .map(this::toPackageName) > .flatMap(Optional::stream) > .collect(Collectors.toSet()); > } catch (IOException ioe) { > throw new UncheckedIOException(ioe); > } > } > > private Optional<String> toPackageName(String name) { > int index = name.lastIndexOf("/"); > if (index > 0) { > return Optional.of(name.substring(0, >index).replace('/', '.')); > } else { > return Optional.empty(); > } > } > > class WarModuleReader implements ModuleReader { > private volatile boolean closed; > > private void ensureOpen() throws IOException { > if (closed) throw new IOException("ModuleReader is >closed"); > } > > public Optional<URI> find(String name) throws IOException { > ensureOpen(); > if (!name.startsWith("/")) { > Path entry = classes.resolve(name); > if (Files.exists(entry)) { > return Optional.of(entry.toUri()); > } > } > return Optional.empty(); > } > > public Stream<String> list() throws IOException { > ensureOpen(); > return Files.walk(classes) > .map(entry -> classes.relativize(entry).toString()) > .filter(name -> name.length() > 0); > } > > public void close() { > closed = true; > } > } > } > -- Alex Sviridov