fixed JPMS bug
This commit is contained in:
@@ -33,7 +33,6 @@ allprojects {
|
|||||||
dependencies {
|
dependencies {
|
||||||
add("testImplementation", create(group: "org.junit.jupiter", name:"junit-jupiter-api", version: project["version.junitJupiter"]))
|
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("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) {
|
tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) {
|
||||||
@@ -63,6 +62,8 @@ configurations {
|
|||||||
dependencies {
|
dependencies {
|
||||||
tar project(path: "launcher", configuration: 'tar')
|
tar project(path: "launcher", configuration: 'tar')
|
||||||
embedded project(path: "common", configuration: "archives")
|
embedded project(path: "common", configuration: "archives")
|
||||||
|
|
||||||
|
testImplementation gradleTestKit()
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('processResources', ProcessResources) {
|
tasks.named('processResources', ProcessResources) {
|
||||||
|
@@ -1,5 +0,0 @@
|
|||||||
jar {
|
|
||||||
manifest {
|
|
||||||
attributes "Automatic-Module-Name" : "net.woggioni.envelope"
|
|
||||||
}
|
|
||||||
}
|
|
@@ -58,4 +58,14 @@ Provider<Tar> tarTaskProvider = tasks.register("tar", Tar) {
|
|||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
tar tarTaskProvider
|
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))
|
||||||
}
|
}
|
@@ -7,7 +7,6 @@ import java.net.URL;
|
|||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
class MainRunner {
|
class MainRunner {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@@ -16,6 +15,14 @@ class MainRunner {
|
|||||||
String mainClassName,
|
String mainClassName,
|
||||||
List<JarFile> classpath,
|
List<JarFile> classpath,
|
||||||
Consumer<Class<?>> runner) {
|
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);
|
URL[] urls = classpath.stream().map(Launcher::getURL).toArray(URL[]::new);
|
||||||
try (URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader().getParent())) {
|
try (URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader().getParent())) {
|
||||||
Thread.currentThread().setContextClassLoader(cl);
|
Thread.currentThread().setContextClassLoader(cl);
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
module net.woggioni.envelope {
|
module net.woggioni.envelope {
|
||||||
requires java.logging;
|
requires java.logging;
|
||||||
requires static lombok;
|
requires static lombok;
|
||||||
requires net.woggioni.envelope.loader;
|
|
||||||
requires java.instrument;
|
requires java.instrument;
|
||||||
}
|
}
|
@@ -19,6 +19,7 @@ import java.net.URLClassLoader;
|
|||||||
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import net.woggioni.envelope.Constants;
|
||||||
import net.woggioni.envelope.loader.ModuleClassLoader;
|
import net.woggioni.envelope.loader.ModuleClassLoader;
|
||||||
import net.woggioni.envelope.loader.JarFileModuleFinder;
|
import net.woggioni.envelope.loader.JarFileModuleFinder;
|
||||||
import net.woggioni.envelope.loader.JarFile;
|
import net.woggioni.envelope.loader.JarFile;
|
||||||
@@ -37,6 +38,14 @@ class MainRunner {
|
|||||||
List<JarFile> classpath,
|
List<JarFile> classpath,
|
||||||
Consumer<Class<?>> runner) {
|
Consumer<Class<?>> runner) {
|
||||||
if(mainModuleName == null) {
|
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);
|
URL[] urls = classpath.stream().map(Launcher::getURL).toArray(URL[]::new);
|
||||||
try (URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader().getParent())) {
|
try (URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader().getParent())) {
|
||||||
Thread.currentThread().setContextClassLoader(cl);
|
Thread.currentThread().setContextClassLoader(cl);
|
||||||
@@ -67,6 +76,7 @@ class MainRunner {
|
|||||||
ModuleLayer layer = controller.layer();
|
ModuleLayer layer = controller.layer();
|
||||||
Module mainModule = layer.findModule(mainModuleName).orElseThrow(
|
Module mainModule = layer.findModule(mainModuleName).orElseThrow(
|
||||||
() -> new IllegalStateException(String.format("Main module '%s' not found", mainModuleName)));
|
() -> new IllegalStateException(String.format("Main module '%s' not found", mainModuleName)));
|
||||||
|
Optional<String> mainClassOpt = Optional.ofNullable(mainClassName);
|
||||||
runner.accept(Optional.ofNullable(mainClassName)
|
runner.accept(Optional.ofNullable(mainClassName)
|
||||||
.or(() -> mainModule.getDescriptor().mainClass())
|
.or(() -> mainModule.getDescriptor().mainClass())
|
||||||
.map(className -> Class.forName(mainModule, className))
|
.map(className -> Class.forName(mainModule, className))
|
||||||
|
@@ -4,4 +4,8 @@ plugins {
|
|||||||
|
|
||||||
ext {
|
ext {
|
||||||
setProperty('jpms.module.name', 'net.woggioni.envelope.loader')
|
setProperty('jpms.module.name', 'net.woggioni.envelope.loader')
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava11 {
|
||||||
|
exclude('module-info.java')
|
||||||
}
|
}
|
@@ -35,8 +35,8 @@ abstract class AbstractJarFile extends java.util.jar.JarFile {
|
|||||||
* @param file the root jar file.
|
* @param file the root jar file.
|
||||||
* @throws IOException on IO error
|
* @throws IOException on IO error
|
||||||
*/
|
*/
|
||||||
AbstractJarFile(File file) throws IOException {
|
AbstractJarFile(File file, boolean verify, int mode) throws IOException {
|
||||||
super(file);
|
super(file, verify, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -38,6 +38,7 @@ import java.util.jar.Manifest;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
import java.util.zip.ZipEntry;
|
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
|
* 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,
|
private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data, JarEntryFilter filter,
|
||||||
JarFileType type, Supplier<Manifest> manifestSupplier) throws IOException {
|
JarFileType type, Supplier<Manifest> manifestSupplier) throws IOException {
|
||||||
super(rootFile.getFile());
|
super(rootFile.getFile(), true, ZipFile.OPEN_READ);
|
||||||
this.rootFile = rootFile;
|
this.rootFile = rootFile;
|
||||||
this.pathFromRoot = pathFromRoot;
|
this.pathFromRoot = pathFromRoot;
|
||||||
CentralDirectoryParser parser = new CentralDirectoryParser();
|
CentralDirectoryParser parser = new CentralDirectoryParser();
|
||||||
|
@@ -26,6 +26,7 @@ import java.util.jar.JarEntry;
|
|||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.zip.ZipEntry;
|
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
|
* 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;
|
private final JarFile parent;
|
||||||
|
|
||||||
JarFileWrapper(JarFile parent) throws IOException {
|
JarFileWrapper(JarFile parent) throws IOException {
|
||||||
super(parent.getRootJarFile().getFile());
|
super(parent.getRootJarFile().getFile(), true, ZipFile.OPEN_READ);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -44,7 +44,6 @@ public class JarFileModuleFinder implements ModuleFinder {
|
|||||||
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
|
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
|
||||||
static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
|
static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<String, Map.Entry<ModuleReference, Handler>> modules;
|
private final Map<String, Map.Entry<ModuleReference, Handler>> modules;
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@@ -122,12 +121,9 @@ public class JarFileModuleFinder implements ModuleFinder {
|
|||||||
public JarFileModuleFinder(JarFile ...jarFiles) {
|
public JarFileModuleFinder(JarFile ...jarFiles) {
|
||||||
this(Arrays.asList(jarFiles));
|
this(Arrays.asList(jarFiles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final String META_INF_VERSION_PREFIX = "META-INF/versions/";
|
|
||||||
private static Set<String> collectPackageNames(JarFile jarFile) {
|
private static Set<String> collectPackageNames(JarFile jarFile) {
|
||||||
Set<String> result = jarFile
|
Set<String> result = jarFile
|
||||||
.stream()
|
.versionedStream()
|
||||||
.filter(entry -> entry.getName().endsWith(".class"))
|
.filter(entry -> entry.getName().endsWith(".class"))
|
||||||
.map(entry -> {
|
.map(entry -> {
|
||||||
String entryName = entry.getName();
|
String entryName = entry.getName();
|
||||||
|
@@ -25,3 +25,4 @@ rootProject.name = 'envelope'
|
|||||||
include 'common'
|
include 'common'
|
||||||
include 'launcher'
|
include 'launcher'
|
||||||
include 'loader'
|
include 'loader'
|
||||||
|
|
||||||
|
@@ -58,7 +58,7 @@ public class EnvelopeJarTask extends AbstractArchiveTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter(onMethod_ = {@Input})
|
@Getter(onMethod_ = {@Input, @Optional})
|
||||||
private final Property<String> mainClass;
|
private final Property<String> mainClass;
|
||||||
|
|
||||||
@Getter(onMethod_ = {@Input, @Optional})
|
@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("Launcher-Agent-Class"), Constants.AGENT_LAUNCHER);
|
||||||
mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");
|
mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");
|
||||||
mainAttributes.put(new Attributes.Name("Can-Retransform-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()) {
|
if(mainModule.isPresent()) {
|
||||||
mainAttributes.putValue(Constants.ManifestAttributes.MAIN_MODULE, mainModule.get());
|
mainAttributes.putValue(Constants.ManifestAttributes.MAIN_MODULE, mainModule.get());
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user