fixed JPMS bug
This commit is contained in:
@@ -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) {
|
||||
|
@@ -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 {
|
||||
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.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);
|
||||
|
@@ -1,6 +1,5 @@
|
||||
module net.woggioni.envelope {
|
||||
requires java.logging;
|
||||
requires static lombok;
|
||||
requires net.woggioni.envelope.loader;
|
||||
requires java.instrument;
|
||||
}
|
@@ -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))
|
||||
|
@@ -4,4 +4,8 @@ plugins {
|
||||
|
||||
ext {
|
||||
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.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 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();
|
||||
|
@@ -25,3 +25,4 @@ rootProject.name = 'envelope'
|
||||
include 'common'
|
||||
include 'launcher'
|
||||
include 'loader'
|
||||
|
||||
|
@@ -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());
|
||||
}
|
||||
|
Reference in New Issue
Block a user