fixed JPMS bug

This commit is contained in:
2022-07-01 04:36:02 +08:00
parent 2b8e150949
commit 2516139a31
14 changed files with 166 additions and 19 deletions

View File

@@ -33,7 +33,6 @@ allprojects {
dependencies {
add("testImplementation", create(group: "org.junit.jupiter", name:"junit-jupiter-api", version: project["version.junitJupiter"]))
add("testRuntimeOnly", create(group: "org.junit.jupiter", name: "junit-jupiter-engine", version: project["version.junitJupiter"]))
add("testImplementation", gradleTestKit())
}
tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) {
@@ -63,6 +62,8 @@ configurations {
dependencies {
tar project(path: "launcher", configuration: 'tar')
embedded project(path: "common", configuration: "archives")
testImplementation gradleTestKit()
}
tasks.named('processResources', ProcessResources) {

View File

@@ -1,5 +0,0 @@
jar {
manifest {
attributes "Automatic-Module-Name" : "net.woggioni.envelope"
}
}

View File

@@ -58,4 +58,14 @@ Provider<Tar> tarTaskProvider = tasks.register("tar", Tar) {
artifacts {
tar tarTaskProvider
}
compileJava11 {
options.javaModuleMainClass = 'net.woggioni.envelope.Launcher'
options.compilerArgs += ['--add-reads', 'net.woggioni.envelope=ALL-UNNAMED']
}
multiReleaseJar {
patchModule('net.woggioni.envelope', project(':common').jar.archiveFile
.map(RegularFile.&getAsFile).map(File.&toString))
}

View File

@@ -7,7 +7,6 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
class MainRunner {
@SneakyThrows
@@ -16,6 +15,14 @@ class MainRunner {
String mainClassName,
List<JarFile> classpath,
Consumer<Class<?>> runner) {
if(mainClassName == null) {
throw new RuntimeException(
String.format(
"Missing main attribute '%s' from manifest",
Constants.ManifestAttributes.MAIN_CLASS
)
);
}
URL[] urls = classpath.stream().map(Launcher::getURL).toArray(URL[]::new);
try (URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader().getParent())) {
Thread.currentThread().setContextClassLoader(cl);

View File

@@ -1,6 +1,5 @@
module net.woggioni.envelope {
requires java.logging;
requires static lombok;
requires net.woggioni.envelope.loader;
requires java.instrument;
}

View File

@@ -19,6 +19,7 @@ import java.net.URLClassLoader;
import lombok.SneakyThrows;
import net.woggioni.envelope.Constants;
import net.woggioni.envelope.loader.ModuleClassLoader;
import net.woggioni.envelope.loader.JarFileModuleFinder;
import net.woggioni.envelope.loader.JarFile;
@@ -37,6 +38,14 @@ class MainRunner {
List<JarFile> classpath,
Consumer<Class<?>> runner) {
if(mainModuleName == null) {
if(mainClassName == null) {
throw new RuntimeException(
String.format(
"Missing main attribute '%s' from manifest",
Constants.ManifestAttributes.MAIN_CLASS
)
);
}
URL[] urls = classpath.stream().map(Launcher::getURL).toArray(URL[]::new);
try (URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader().getParent())) {
Thread.currentThread().setContextClassLoader(cl);
@@ -67,6 +76,7 @@ class MainRunner {
ModuleLayer layer = controller.layer();
Module mainModule = layer.findModule(mainModuleName).orElseThrow(
() -> new IllegalStateException(String.format("Main module '%s' not found", mainModuleName)));
Optional<String> mainClassOpt = Optional.ofNullable(mainClassName);
runner.accept(Optional.ofNullable(mainClassName)
.or(() -> mainModule.getDescriptor().mainClass())
.map(className -> Class.forName(mainModule, className))

View File

@@ -4,4 +4,8 @@ plugins {
ext {
setProperty('jpms.module.name', 'net.woggioni.envelope.loader')
}
compileJava11 {
exclude('module-info.java')
}

View File

@@ -35,8 +35,8 @@ abstract class AbstractJarFile extends java.util.jar.JarFile {
* @param file the root jar file.
* @throws IOException on IO error
*/
AbstractJarFile(File file) throws IOException {
super(file);
AbstractJarFile(File file, boolean verify, int mode) throws IOException {
super(file, verify, mode);
}
/**

View File

@@ -38,6 +38,7 @@ import java.util.jar.Manifest;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Extended variant of {@link java.util.jar.JarFile} that behaves in the same way but
@@ -129,7 +130,7 @@ public class JarFile extends AbstractJarFile implements Iterable<java.util.jar.J
private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data, JarEntryFilter filter,
JarFileType type, Supplier<Manifest> manifestSupplier) throws IOException {
super(rootFile.getFile());
super(rootFile.getFile(), true, ZipFile.OPEN_READ);
this.rootFile = rootFile;
this.pathFromRoot = pathFromRoot;
CentralDirectoryParser parser = new CentralDirectoryParser();

View File

@@ -26,6 +26,7 @@ import java.util.jar.JarEntry;
import java.util.jar.Manifest;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* A wrapper used to create a copy of a {@link JarFile} so that it can be safely closed
@@ -38,7 +39,7 @@ class JarFileWrapper extends AbstractJarFile {
private final JarFile parent;
JarFileWrapper(JarFile parent) throws IOException {
super(parent.getRootJarFile().getFile());
super(parent.getRootJarFile().getFile(), true, ZipFile.OPEN_READ);
this.parent = parent;
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.woggioni.envelope.loader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Permission;
import java.util.Objects;
import java.util.zip.ZipFile;
import java.util.stream.Stream;
import java.util.jar.JarEntry;
/**
* Base class for extended variants of {@link java.util.jar.JarFile}.
*
* @author Phillip Webb
*/
abstract class AbstractJarFile extends java.util.jar.JarFile {
private static final String META_INF_VERSION_PREFIX = "META-INF/versions/";
private final Runtime.Version version; // current version
private final int versionFeature; // version.feature()
private String getBasename(String name) {
if (name.startsWith(META_INF_VERSION_PREFIX)) {
int off = META_INF_VERSION_PREFIX.length();
int index = name.indexOf('/', off);
try {
// filter out dir META-INF/versions/ and META-INF/versions/*/
// and any entry with version > 'version'
if (index == -1 || index == (name.length() - 1) ||
Integer.parseInt(name, off, index, 10) > versionFeature) {
return null;
}
} catch (NumberFormatException x) {
return null; // remove malformed entries silently
}
// map to its base name
return name.substring(index + 1);
}
return name;
}
@Override
public Stream<JarEntry> versionedStream() {
return stream()
.map(JarEntry::getName)
.map(this::getBasename)
.filter(Objects::nonNull)
.distinct()
.map(this::getJarEntry)
.filter(Objects::nonNull);
}
/**
* Create a new {@link AbstractJarFile}.
* @param file the root jar file.
* @throws IOException on IO error
*/
AbstractJarFile(File file, boolean verify, int mode) throws IOException {
super(file, verify, mode);
this.version = Runtime.version();
this.versionFeature = this.version.feature();
}
/**
* Return a URL that can be used to access this JAR file. NOTE: the specified URL
* cannot be serialized and or cloned.
* @return the URL
* @throws MalformedURLException if the URL is malformed
*/
abstract URL getUrl() throws MalformedURLException;
/**
* Return the {@link JarFileType} of this instance.
* @return the jar file type
*/
abstract JarFileType getType();
/**
* Return the security permission for this JAR.
* @return the security permission.
*/
abstract Permission getPermission();
/**
* Return an {@link InputStream} for the entire jar contents.
* @return the contents input stream
* @throws IOException on IO error
*/
abstract InputStream getInputStream() throws IOException;
/**
* The type of a {@link JarFile}.
*/
enum JarFileType {
DIRECT, NESTED_DIRECTORY, NESTED_JAR
}
}

View File

@@ -44,7 +44,6 @@ public class JarFileModuleFinder implements ModuleFinder {
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
}
private final Map<String, Map.Entry<ModuleReference, Handler>> modules;
@SneakyThrows
@@ -122,12 +121,9 @@ public class JarFileModuleFinder implements ModuleFinder {
public JarFileModuleFinder(JarFile ...jarFiles) {
this(Arrays.asList(jarFiles));
}
private static final String META_INF_VERSION_PREFIX = "META-INF/versions/";
private static Set<String> collectPackageNames(JarFile jarFile) {
Set<String> result = jarFile
.stream()
.versionedStream()
.filter(entry -> entry.getName().endsWith(".class"))
.map(entry -> {
String entryName = entry.getName();

View File

@@ -25,3 +25,4 @@ rootProject.name = 'envelope'
include 'common'
include 'launcher'
include 'loader'

View File

@@ -58,7 +58,7 @@ public class EnvelopeJarTask extends AbstractArchiveTask {
}
}
@Getter(onMethod_ = {@Input})
@Getter(onMethod_ = {@Input, @Optional})
private final Property<String> mainClass;
@Getter(onMethod_ = {@Input, @Optional})
@@ -235,7 +235,9 @@ public class EnvelopeJarTask extends AbstractArchiveTask {
mainAttributes.put(new Attributes.Name("Launcher-Agent-Class"), Constants.AGENT_LAUNCHER);
mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");
mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"), "true");
mainAttributes.putValue(Constants.ManifestAttributes.MAIN_CLASS, mainClass.get());
if(mainClass.isPresent()) {
mainAttributes.putValue(Constants.ManifestAttributes.MAIN_CLASS, mainClass.get());
}
if(mainModule.isPresent()) {
mainAttributes.putValue(Constants.ManifestAttributes.MAIN_MODULE, mainModule.get());
}