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;
            }
        }
    }

Reply via email to