mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 16:41:46 +03:00
[FEATURE] Use Java 17
This commit is contained in:
parent
a0ac58f0b5
commit
1cbf95f815
15 changed files with 371 additions and 766 deletions
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"features": [],
|
||||
"features": ["java17"],
|
||||
"info": []
|
||||
}
|
|
@ -10,11 +10,11 @@
|
|||
}
|
||||
}
|
||||
javafx {
|
||||
version = "12"
|
||||
version = "17"
|
||||
modules = ['javafx.controls', 'javafx.fxml']
|
||||
}
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
configurations {
|
||||
bundle
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
dependencies {
|
||||
api project(':LauncherCore')
|
||||
|
@ -15,28 +15,12 @@ api project(':LauncherCore')
|
|||
}
|
||||
|
||||
sourceSets {
|
||||
java11 {
|
||||
java {
|
||||
srcDirs = ['src/main/java11']
|
||||
}
|
||||
dependencies {
|
||||
java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
into('META-INF/versions/11') {
|
||||
from sourceSets.java11.output
|
||||
}
|
||||
archiveClassifier.set('clean')
|
||||
}
|
||||
|
||||
compileJava11Java {
|
||||
sourceCompatibility = 11
|
||||
targetCompatibility = 11
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.allJava
|
||||
archiveClassifier.set('sources')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
|
||||
|
@ -22,26 +22,10 @@
|
|||
}
|
||||
}
|
||||
sourceSets {
|
||||
java11 {
|
||||
java {
|
||||
srcDirs = ['src/main/java11']
|
||||
}
|
||||
dependencies {
|
||||
java11Implementation group: 'com.google.code.gson', name: 'gson', version: rootProject['verGson']
|
||||
java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }
|
||||
}
|
||||
}
|
||||
}
|
||||
jar {
|
||||
into('META-INF/versions/11') {
|
||||
from sourceSets.java11.output
|
||||
}
|
||||
archiveClassifier.set('clean')
|
||||
}
|
||||
compileJava11Java {
|
||||
sourceCompatibility = 11
|
||||
targetCompatibility = 11
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.allJava
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
|
||||
public final class HTTPRequest {
|
||||
private static final int TIMEOUT = 10000;
|
||||
|
@ -22,36 +25,36 @@ public static JsonElement jsonRequest(JsonElement request, URL url) throws IOExc
|
|||
}
|
||||
|
||||
public static JsonElement jsonRequest(JsonElement request, String method, URL url) throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setDoInput(true);
|
||||
if (request != null) connection.setDoOutput(true);
|
||||
connection.setRequestMethod(method);
|
||||
if (request != null) connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
|
||||
if (request != null) connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36");
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
if (TIMEOUT > 0)
|
||||
connection.setConnectTimeout(TIMEOUT);
|
||||
if (request != null)
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8)) {
|
||||
writer.write(request.toString());
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
InputStreamReader reader;
|
||||
int statusCode = connection.getResponseCode();
|
||||
|
||||
if (200 <= statusCode && statusCode < 300)
|
||||
reader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8);
|
||||
else
|
||||
reader = new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8);
|
||||
HttpClient client = HttpClient.newBuilder()
|
||||
.build();
|
||||
HttpRequest.BodyPublisher publisher;
|
||||
if (request != null) {
|
||||
publisher = HttpRequest.BodyPublishers.ofString(request.toString());
|
||||
} else {
|
||||
publisher = HttpRequest.BodyPublishers.noBody();
|
||||
}
|
||||
try {
|
||||
return JsonParser.parseReader(reader);
|
||||
} catch (Exception e) {
|
||||
if (200 > statusCode || statusCode > 300) {
|
||||
LogHelper.error("JsonRequest failed. Server response code %d", statusCode);
|
||||
throw new IOException(e);
|
||||
HttpRequest request1 = HttpRequest.newBuilder()
|
||||
.method(method, publisher)
|
||||
.uri(url.toURI())
|
||||
.header("Content-Type", "application/json; charset=UTF-8")
|
||||
.header("Accept", "application/json")
|
||||
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36")
|
||||
.timeout(Duration.ofMillis(TIMEOUT))
|
||||
.build();
|
||||
HttpResponse<InputStream> response = client.send(request1, HttpResponse.BodyHandlers.ofInputStream());
|
||||
int statusCode = response.statusCode();
|
||||
try {
|
||||
return JsonParser.parseReader(IOHelper.newReader(response.body()));
|
||||
} catch (Exception e) {
|
||||
if (200 > statusCode || statusCode > 300) {
|
||||
LogHelper.error("JsonRequest failed. Server response code %d", statusCode);
|
||||
throw new IOException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
} catch (URISyntaxException | InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,57 +1,160 @@
|
|||
package pro.gravit.utils;
|
||||
|
||||
import pro.gravit.launcher.AsyncDownloader;
|
||||
import pro.gravit.launcher.LauncherInject;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Downloader {
|
||||
private final CompletableFuture<Void> future;
|
||||
private final AsyncDownloader asyncDownloader;
|
||||
private Downloader(CompletableFuture<Void> future, AsyncDownloader downloader) {
|
||||
this.future = future;
|
||||
this.asyncDownloader = downloader;
|
||||
@LauncherInject("launcher.certificatePinning")
|
||||
private static boolean isCertificatePinning;
|
||||
@LauncherInject("launcher.noHttp2")
|
||||
private static boolean isNoHttp2;
|
||||
protected final HttpClient client;
|
||||
protected final ExecutorService executor;
|
||||
protected final LinkedList<DownloadTask> tasks = new LinkedList<>();
|
||||
protected CompletableFuture<Void> future;
|
||||
protected Downloader(HttpClient client, ExecutorService executor) {
|
||||
this.client = client;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public static Downloader downloadList(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
||||
final boolean closeExecutor;
|
||||
LogHelper.info("Download with legacy mode");
|
||||
boolean closeExecutor = false;
|
||||
LogHelper.info("Download with Java 11+ HttpClient");
|
||||
if (executor == null) {
|
||||
executor = Executors.newWorkStealingPool(4);
|
||||
executor = Executors.newWorkStealingPool(Math.min(3, threads));
|
||||
closeExecutor = true;
|
||||
} else {
|
||||
closeExecutor = false;
|
||||
}
|
||||
AsyncDownloader asyncDownloader = new AsyncDownloader((diff) -> {
|
||||
if (callback != null) {
|
||||
callback.apply(diff);
|
||||
}
|
||||
});
|
||||
List<List<AsyncDownloader.SizedFile>> list = asyncDownloader.sortFiles(files, threads);
|
||||
CompletableFuture<Void> future = CompletableFuture.allOf(asyncDownloader.runDownloadList(list, baseURL, targetDir, executor));
|
||||
Downloader downloader = newDownloader(executor);
|
||||
downloader.future = downloader.downloadFiles(files, baseURL, targetDir, callback, executor, threads);
|
||||
if (closeExecutor) {
|
||||
ExecutorService finalExecutor = executor;
|
||||
downloader.future = downloader.future.thenAccept(e -> finalExecutor.shutdownNow());
|
||||
}
|
||||
return downloader;
|
||||
}
|
||||
|
||||
ExecutorService finalExecutor = executor;
|
||||
return new Downloader(future.thenAccept(e -> {
|
||||
if (closeExecutor) {
|
||||
finalExecutor.shutdownNow();
|
||||
public static Downloader newDownloader(ExecutorService executor) {
|
||||
if (executor == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
HttpClient.Builder builder = HttpClient.newBuilder()
|
||||
.version(isNoHttp2 ? HttpClient.Version.HTTP_1_1 : HttpClient.Version.HTTP_2)
|
||||
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||
.executor(executor);
|
||||
if (isCertificatePinning) {
|
||||
try {
|
||||
builder.sslContext(AsyncDownloader.makeSSLContext());
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}), asyncDownloader);
|
||||
}
|
||||
HttpClient client = builder.build();
|
||||
return new Downloader(client, executor);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
for (DownloadTask task : tasks) {
|
||||
if (!task.isCompleted()) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
tasks.clear();
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return executor.isTerminated();
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> getFuture() {
|
||||
return future;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
this.asyncDownloader.isClosed = true;
|
||||
public CompletableFuture<Void> downloadFiles(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
||||
// URI scheme
|
||||
URI baseUri = new URI(baseURL);
|
||||
Collections.shuffle(files);
|
||||
Queue<AsyncDownloader.SizedFile> queue = new ConcurrentLinkedDeque<>(files);
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
AtomicInteger currentThreads = new AtomicInteger(threads);
|
||||
ConsumerObject consumerObject = new ConsumerObject();
|
||||
Consumer<HttpResponse<Path>> next = e -> {
|
||||
if (callback != null && e != null) {
|
||||
callback.onComplete(e.body());
|
||||
}
|
||||
AsyncDownloader.SizedFile file = queue.poll();
|
||||
if (file == null) {
|
||||
if (currentThreads.decrementAndGet() == 0)
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
DownloadTask task = sendAsync(file, baseUri, targetDir, callback);
|
||||
task.completableFuture.thenAccept(consumerObject.next).exceptionally(ec -> {
|
||||
future.completeExceptionally(ec);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
LogHelper.error(exception);
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
};
|
||||
consumerObject.next = next;
|
||||
for (int i = 0; i < threads; ++i) {
|
||||
next.accept(null);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return this.asyncDownloader.isClosed;
|
||||
protected DownloadTask sendAsync(AsyncDownloader.SizedFile file, URI baseUri, Path targetDir, DownloadCallback callback) throws Exception {
|
||||
IOHelper.createParentDirs(targetDir.resolve(file.filePath));
|
||||
ProgressTrackingBodyHandler<Path> bodyHandler = makeBodyHandler(targetDir.resolve(file.filePath), callback);
|
||||
CompletableFuture<HttpResponse<Path>> future = client.sendAsync(makeHttpRequest(baseUri, file.urlPath), bodyHandler);
|
||||
var ref = new Object() {
|
||||
DownloadTask task = null;
|
||||
};
|
||||
ref.task = new DownloadTask(bodyHandler, future.thenApply((e) -> {
|
||||
tasks.remove(ref.task);
|
||||
return e;
|
||||
}));
|
||||
tasks.add(ref.task);
|
||||
return ref.task;
|
||||
}
|
||||
|
||||
protected HttpRequest makeHttpRequest(URI baseUri, String filePath) throws URISyntaxException {
|
||||
String scheme = baseUri.getScheme();
|
||||
String host = baseUri.getHost();
|
||||
int port = baseUri.getPort();
|
||||
if (port != -1)
|
||||
host = host + ":" + port;
|
||||
String path = baseUri.getPath();
|
||||
return HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(new URI(scheme, host, path + filePath, "", ""))
|
||||
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36")
|
||||
.build();
|
||||
}
|
||||
|
||||
protected ProgressTrackingBodyHandler<Path> makeBodyHandler(Path file, DownloadCallback callback) {
|
||||
return new ProgressTrackingBodyHandler<>(HttpResponse.BodyHandlers.ofFile(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE), callback);
|
||||
}
|
||||
|
||||
public interface DownloadCallback {
|
||||
|
@ -59,4 +162,105 @@ public interface DownloadCallback {
|
|||
|
||||
void onComplete(Path path);
|
||||
}
|
||||
|
||||
private static class ConsumerObject {
|
||||
Consumer<HttpResponse<Path>> next = null;
|
||||
}
|
||||
|
||||
public static class DownloadTask {
|
||||
public final ProgressTrackingBodyHandler<Path> bodyHandler;
|
||||
public final CompletableFuture<HttpResponse<Path>> completableFuture;
|
||||
|
||||
public DownloadTask(ProgressTrackingBodyHandler<Path> bodyHandler, CompletableFuture<HttpResponse<Path>> completableFuture) {
|
||||
this.bodyHandler = bodyHandler;
|
||||
this.completableFuture = completableFuture;
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return completableFuture.isDone() | completableFuture.isCompletedExceptionally();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
bodyHandler.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProgressTrackingBodyHandler<T> implements HttpResponse.BodyHandler<T> {
|
||||
private final HttpResponse.BodyHandler<T> delegate;
|
||||
private final DownloadCallback callback;
|
||||
private ProgressTrackingBodySubscriber subscriber;
|
||||
private boolean isCanceled = false;
|
||||
|
||||
public ProgressTrackingBodyHandler(HttpResponse.BodyHandler<T> delegate, DownloadCallback callback) {
|
||||
this.delegate = delegate;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse.BodySubscriber<T> apply(HttpResponse.ResponseInfo responseInfo) {
|
||||
subscriber = new ProgressTrackingBodySubscriber(delegate.apply(responseInfo));
|
||||
if (isCanceled) {
|
||||
subscriber.cancel();
|
||||
}
|
||||
return subscriber;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
isCanceled = true;
|
||||
if (subscriber != null) {
|
||||
subscriber.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private class ProgressTrackingBodySubscriber implements HttpResponse.BodySubscriber<T> {
|
||||
private final HttpResponse.BodySubscriber<T> delegate;
|
||||
private Flow.Subscription subscription;
|
||||
private boolean isCanceled = false;
|
||||
|
||||
public ProgressTrackingBodySubscriber(HttpResponse.BodySubscriber<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<T> getBody() {
|
||||
return delegate.getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(Flow.Subscription subscription) {
|
||||
this.subscription = subscription;
|
||||
if (isCanceled) {
|
||||
subscription.cancel();
|
||||
}
|
||||
delegate.onSubscribe(subscription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<ByteBuffer> byteBuffers) {
|
||||
long diff = 0;
|
||||
for (ByteBuffer buffer : byteBuffers) {
|
||||
diff += buffer.remaining();
|
||||
}
|
||||
if (callback != null) callback.apply(diff);
|
||||
delegate.onNext(byteBuffers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
delegate.onError(throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
delegate.onComplete();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
isCanceled = true;
|
||||
if (subscription != null) {
|
||||
subscription.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,15 @@ public final class JVMHelper {
|
|||
public static final OperatingSystemMXBean OPERATING_SYSTEM_MXBEAN =
|
||||
ManagementFactory.getOperatingSystemMXBean();
|
||||
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
||||
@Deprecated
|
||||
public static final int OS_BITS = getCorrectOSArch();
|
||||
// System properties
|
||||
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
||||
|
||||
@Deprecated
|
||||
public static final int OS_BITS = getCorrectOSArch();
|
||||
|
||||
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
|
||||
|
||||
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
||||
public static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
|
||||
// Public static fields
|
||||
public static final Runtime RUNTIME = Runtime.getRuntime();
|
||||
public static final ClassLoader LOADER = ClassLoader.getSystemClassLoader();
|
||||
|
@ -44,44 +46,31 @@ public final class JVMHelper {
|
|||
private JVMHelper() {
|
||||
}
|
||||
|
||||
public enum ARCH {
|
||||
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
||||
|
||||
public final String name;
|
||||
|
||||
ARCH(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static ARCH getArch(String arch) {
|
||||
if (arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
|
||||
if (arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
|
||||
if (arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
|
||||
if (arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
|
||||
if(arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
|
||||
if(arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
|
||||
if(arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
|
||||
if(arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
|
||||
throw new InternalError(String.format("Unsupported arch '%s'", arch));
|
||||
}
|
||||
|
||||
public static int getVersion() {
|
||||
String version = System.getProperty("java.version");
|
||||
if (version.startsWith("1.")) {
|
||||
version = version.substring(2, 3);
|
||||
} else {
|
||||
int dot = version.indexOf(".");
|
||||
if (dot != -1) {
|
||||
version = version.substring(0, dot);
|
||||
}
|
||||
}
|
||||
return Integer.parseInt(version);
|
||||
//System.out.println("[DEBUG] JVMHelper 11 version");
|
||||
return Runtime.version().feature();
|
||||
}
|
||||
|
||||
public static int getBuild() {
|
||||
String version = System.getProperty("java.version");
|
||||
int dot;
|
||||
if (version.startsWith("1.")) {
|
||||
dot = version.indexOf("_");
|
||||
} else {
|
||||
dot = version.lastIndexOf(".");
|
||||
}
|
||||
if (dot != -1) {
|
||||
version = version.substring(dot + 1);
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(version);
|
||||
} catch (NumberFormatException exception) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Runtime.version().update();
|
||||
}
|
||||
|
||||
public static void appendVars(ProcessBuilder builder, Map<String, String> vars) {
|
||||
|
@ -98,16 +87,19 @@ public static Class<?> firstClass(String... names) throws ClassNotFoundException
|
|||
throw new ClassNotFoundException(Arrays.toString(names));
|
||||
}
|
||||
|
||||
|
||||
public static void fullGC() {
|
||||
RUNTIME.gc();
|
||||
RUNTIME.runFinalization();
|
||||
LogHelper.debug("Used heap: %d MiB", RUNTIME.totalMemory() - RUNTIME.freeMemory() >> 20);
|
||||
}
|
||||
|
||||
|
||||
public static String[] getClassPath() {
|
||||
return System.getProperty("java.class.path").split(File.pathSeparator);
|
||||
}
|
||||
|
||||
|
||||
public static URL[] getClassPathURL() {
|
||||
String[] cp = System.getProperty("java.class.path").split(File.pathSeparator);
|
||||
URL[] list = new URL[cp.length];
|
||||
|
@ -149,29 +141,35 @@ private static int getCorrectOSArch() {
|
|||
return System.getProperty("os.arch").contains("64") ? 64 : 32;
|
||||
}
|
||||
|
||||
|
||||
public static String getEnvPropertyCaseSensitive(String name) {
|
||||
return System.getenv().get(name);
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static boolean isJVMMatchesSystemArch() {
|
||||
return JVM_BITS == OS_BITS;
|
||||
}
|
||||
|
||||
|
||||
public static String jvmProperty(String name, String value) {
|
||||
return String.format("-D%s=%s", name, value);
|
||||
}
|
||||
|
||||
|
||||
public static String systemToJvmProperty(String name) {
|
||||
return String.format("-D%s=%s", name, System.getProperties().getProperty(name));
|
||||
}
|
||||
|
||||
|
||||
public static void addSystemPropertyToArgs(Collection<String> args, String name) {
|
||||
String property = System.getProperty(name);
|
||||
if (property != null)
|
||||
args.add(String.format("-D%s=%s", name, property));
|
||||
}
|
||||
|
||||
|
||||
public static void verifySystemProperties(Class<?> mainClass, boolean requireSystem) {
|
||||
Locale.setDefault(Locale.US);
|
||||
// Verify class loader
|
||||
|
@ -183,17 +181,6 @@ public static void verifySystemProperties(Class<?> mainClass, boolean requireSys
|
|||
LogHelper.debug("Verifying JVM architecture");
|
||||
}
|
||||
|
||||
|
||||
public enum ARCH {
|
||||
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
||||
|
||||
public final String name;
|
||||
|
||||
ARCH(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public enum OS {
|
||||
MUSTDIE("mustdie"), LINUX("linux"), MACOSX("macosx");
|
||||
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package pro.gravit.launcher;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
|
||||
public final class HTTPRequest {
|
||||
private static final int TIMEOUT = 10000;
|
||||
|
||||
private HTTPRequest() {
|
||||
}
|
||||
|
||||
public static JsonElement jsonRequest(JsonElement request, URL url) throws IOException {
|
||||
return jsonRequest(request, "POST", url);
|
||||
}
|
||||
|
||||
public static JsonElement jsonRequest(JsonElement request, String method, URL url) throws IOException {
|
||||
HttpClient client = HttpClient.newBuilder()
|
||||
.build();
|
||||
HttpRequest.BodyPublisher publisher;
|
||||
if (request != null) {
|
||||
publisher = HttpRequest.BodyPublishers.ofString(request.toString());
|
||||
} else {
|
||||
publisher = HttpRequest.BodyPublishers.noBody();
|
||||
}
|
||||
try {
|
||||
HttpRequest request1 = HttpRequest.newBuilder()
|
||||
.method(method, publisher)
|
||||
.uri(url.toURI())
|
||||
.header("Content-Type", "application/json; charset=UTF-8")
|
||||
.header("Accept", "application/json")
|
||||
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36")
|
||||
.timeout(Duration.ofMillis(TIMEOUT))
|
||||
.build();
|
||||
HttpResponse<InputStream> response = client.send(request1, HttpResponse.BodyHandlers.ofInputStream());
|
||||
int statusCode = response.statusCode();
|
||||
try {
|
||||
return JsonParser.parseReader(IOHelper.newReader(response.body()));
|
||||
} catch (Exception e) {
|
||||
if (200 > statusCode || statusCode > 300) {
|
||||
LogHelper.error("JsonRequest failed. Server response code %d", statusCode);
|
||||
throw new IOException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} catch (URISyntaxException | InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,266 +0,0 @@
|
|||
package pro.gravit.utils;
|
||||
|
||||
import pro.gravit.launcher.AsyncDownloader;
|
||||
import pro.gravit.launcher.LauncherInject;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Downloader {
|
||||
@LauncherInject("launcher.certificatePinning")
|
||||
private static boolean isCertificatePinning;
|
||||
@LauncherInject("launcher.noHttp2")
|
||||
private static boolean isNoHttp2;
|
||||
protected final HttpClient client;
|
||||
protected final ExecutorService executor;
|
||||
protected final LinkedList<DownloadTask> tasks = new LinkedList<>();
|
||||
protected CompletableFuture<Void> future;
|
||||
protected Downloader(HttpClient client, ExecutorService executor) {
|
||||
this.client = client;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public static Downloader downloadList(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
||||
boolean closeExecutor = false;
|
||||
LogHelper.info("Download with Java 11+ HttpClient");
|
||||
if (executor == null) {
|
||||
executor = Executors.newWorkStealingPool(Math.min(3, threads));
|
||||
closeExecutor = true;
|
||||
}
|
||||
Downloader downloader = newDownloader(executor);
|
||||
downloader.future = downloader.downloadFiles(files, baseURL, targetDir, callback, executor, threads);
|
||||
if (closeExecutor) {
|
||||
ExecutorService finalExecutor = executor;
|
||||
downloader.future = downloader.future.thenAccept(e -> finalExecutor.shutdownNow());
|
||||
}
|
||||
return downloader;
|
||||
}
|
||||
|
||||
public static Downloader newDownloader(ExecutorService executor) {
|
||||
if (executor == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
HttpClient.Builder builder = HttpClient.newBuilder()
|
||||
.version(isNoHttp2 ? HttpClient.Version.HTTP_1_1 : HttpClient.Version.HTTP_2)
|
||||
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||
.executor(executor);
|
||||
if (isCertificatePinning) {
|
||||
try {
|
||||
builder.sslContext(AsyncDownloader.makeSSLContext());
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
HttpClient client = builder.build();
|
||||
return new Downloader(client, executor);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
for (DownloadTask task : tasks) {
|
||||
if (!task.isCompleted()) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
tasks.clear();
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return executor.isTerminated();
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> getFuture() {
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> downloadFiles(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
||||
// URI scheme
|
||||
URI baseUri = new URI(baseURL);
|
||||
Collections.shuffle(files);
|
||||
Queue<AsyncDownloader.SizedFile> queue = new ConcurrentLinkedDeque<>(files);
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
AtomicInteger currentThreads = new AtomicInteger(threads);
|
||||
ConsumerObject consumerObject = new ConsumerObject();
|
||||
Consumer<HttpResponse<Path>> next = e -> {
|
||||
if (callback != null && e != null) {
|
||||
callback.onComplete(e.body());
|
||||
}
|
||||
AsyncDownloader.SizedFile file = queue.poll();
|
||||
if (file == null) {
|
||||
if (currentThreads.decrementAndGet() == 0)
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
DownloadTask task = sendAsync(file, baseUri, targetDir, callback);
|
||||
task.completableFuture.thenAccept(consumerObject.next).exceptionally(ec -> {
|
||||
future.completeExceptionally(ec);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
LogHelper.error(exception);
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
};
|
||||
consumerObject.next = next;
|
||||
for (int i = 0; i < threads; ++i) {
|
||||
next.accept(null);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected DownloadTask sendAsync(AsyncDownloader.SizedFile file, URI baseUri, Path targetDir, DownloadCallback callback) throws Exception {
|
||||
IOHelper.createParentDirs(targetDir.resolve(file.filePath));
|
||||
ProgressTrackingBodyHandler<Path> bodyHandler = makeBodyHandler(targetDir.resolve(file.filePath), callback);
|
||||
CompletableFuture<HttpResponse<Path>> future = client.sendAsync(makeHttpRequest(baseUri, file.urlPath), bodyHandler);
|
||||
var ref = new Object() {
|
||||
DownloadTask task = null;
|
||||
};
|
||||
ref.task = new DownloadTask(bodyHandler, future.thenApply((e) -> {
|
||||
tasks.remove(ref.task);
|
||||
return e;
|
||||
}));
|
||||
tasks.add(ref.task);
|
||||
return ref.task;
|
||||
}
|
||||
|
||||
protected HttpRequest makeHttpRequest(URI baseUri, String filePath) throws URISyntaxException {
|
||||
String scheme = baseUri.getScheme();
|
||||
String host = baseUri.getHost();
|
||||
int port = baseUri.getPort();
|
||||
if (port != -1)
|
||||
host = host + ":" + port;
|
||||
String path = baseUri.getPath();
|
||||
return HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(new URI(scheme, host, path + filePath, "", ""))
|
||||
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36")
|
||||
.build();
|
||||
}
|
||||
|
||||
protected ProgressTrackingBodyHandler<Path> makeBodyHandler(Path file, DownloadCallback callback) {
|
||||
return new ProgressTrackingBodyHandler<>(HttpResponse.BodyHandlers.ofFile(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE), callback);
|
||||
}
|
||||
|
||||
public interface DownloadCallback {
|
||||
void apply(long fullDiff);
|
||||
|
||||
void onComplete(Path path);
|
||||
}
|
||||
|
||||
private static class ConsumerObject {
|
||||
Consumer<HttpResponse<Path>> next = null;
|
||||
}
|
||||
|
||||
public static class DownloadTask {
|
||||
public final ProgressTrackingBodyHandler<Path> bodyHandler;
|
||||
public final CompletableFuture<HttpResponse<Path>> completableFuture;
|
||||
|
||||
public DownloadTask(ProgressTrackingBodyHandler<Path> bodyHandler, CompletableFuture<HttpResponse<Path>> completableFuture) {
|
||||
this.bodyHandler = bodyHandler;
|
||||
this.completableFuture = completableFuture;
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return completableFuture.isDone() | completableFuture.isCompletedExceptionally();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
bodyHandler.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProgressTrackingBodyHandler<T> implements HttpResponse.BodyHandler<T> {
|
||||
private final HttpResponse.BodyHandler<T> delegate;
|
||||
private final DownloadCallback callback;
|
||||
private ProgressTrackingBodySubscriber subscriber;
|
||||
private boolean isCanceled = false;
|
||||
|
||||
public ProgressTrackingBodyHandler(HttpResponse.BodyHandler<T> delegate, DownloadCallback callback) {
|
||||
this.delegate = delegate;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse.BodySubscriber<T> apply(HttpResponse.ResponseInfo responseInfo) {
|
||||
subscriber = new ProgressTrackingBodySubscriber(delegate.apply(responseInfo));
|
||||
if (isCanceled) {
|
||||
subscriber.cancel();
|
||||
}
|
||||
return subscriber;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
isCanceled = true;
|
||||
if (subscriber != null) {
|
||||
subscriber.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private class ProgressTrackingBodySubscriber implements HttpResponse.BodySubscriber<T> {
|
||||
private final HttpResponse.BodySubscriber<T> delegate;
|
||||
private Flow.Subscription subscription;
|
||||
private boolean isCanceled = false;
|
||||
|
||||
public ProgressTrackingBodySubscriber(HttpResponse.BodySubscriber<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<T> getBody() {
|
||||
return delegate.getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(Flow.Subscription subscription) {
|
||||
this.subscription = subscription;
|
||||
if (isCanceled) {
|
||||
subscription.cancel();
|
||||
}
|
||||
delegate.onSubscribe(subscription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<ByteBuffer> byteBuffers) {
|
||||
long diff = 0;
|
||||
for (ByteBuffer buffer : byteBuffers) {
|
||||
diff += buffer.remaining();
|
||||
}
|
||||
if (callback != null) callback.apply(diff);
|
||||
delegate.onNext(byteBuffers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
delegate.onError(throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
delegate.onComplete();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
isCanceled = true;
|
||||
if (subscription != null) {
|
||||
subscription.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
package pro.gravit.utils.helper;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public final class JVMHelper {
|
||||
|
||||
// MXBeans exports
|
||||
public static final RuntimeMXBean RUNTIME_MXBEAN = ManagementFactory.getRuntimeMXBean();
|
||||
public static final OperatingSystemMXBean OPERATING_SYSTEM_MXBEAN =
|
||||
ManagementFactory.getOperatingSystemMXBean();
|
||||
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
||||
// System properties
|
||||
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
||||
|
||||
@Deprecated
|
||||
public static final int OS_BITS = getCorrectOSArch();
|
||||
|
||||
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
|
||||
|
||||
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
||||
// Public static fields
|
||||
public static final Runtime RUNTIME = Runtime.getRuntime();
|
||||
public static final ClassLoader LOADER = ClassLoader.getSystemClassLoader();
|
||||
public static final int JVM_VERSION = getVersion();
|
||||
public static final int JVM_BUILD = getBuild();
|
||||
|
||||
static {
|
||||
try {
|
||||
MethodHandles.publicLookup(); // Just to initialize class
|
||||
} catch (Throwable exc) {
|
||||
throw new InternalError(exc);
|
||||
}
|
||||
}
|
||||
|
||||
private JVMHelper() {
|
||||
}
|
||||
|
||||
public enum ARCH {
|
||||
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
||||
|
||||
public final String name;
|
||||
|
||||
ARCH(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static ARCH getArch(String arch) {
|
||||
if(arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
|
||||
if(arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
|
||||
if(arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
|
||||
if(arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
|
||||
throw new InternalError(String.format("Unsupported arch '%s'", arch));
|
||||
}
|
||||
|
||||
public static int getVersion() {
|
||||
//System.out.println("[DEBUG] JVMHelper 11 version");
|
||||
return Runtime.version().feature();
|
||||
}
|
||||
|
||||
public static int getBuild() {
|
||||
return Runtime.version().update();
|
||||
}
|
||||
|
||||
public static void appendVars(ProcessBuilder builder, Map<String, String> vars) {
|
||||
builder.environment().putAll(vars);
|
||||
}
|
||||
|
||||
public static Class<?> firstClass(String... names) throws ClassNotFoundException {
|
||||
for (String name : names)
|
||||
try {
|
||||
return Class.forName(name, false, LOADER);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// Expected
|
||||
}
|
||||
throw new ClassNotFoundException(Arrays.toString(names));
|
||||
}
|
||||
|
||||
|
||||
public static void fullGC() {
|
||||
RUNTIME.gc();
|
||||
RUNTIME.runFinalization();
|
||||
LogHelper.debug("Used heap: %d MiB", RUNTIME.totalMemory() - RUNTIME.freeMemory() >> 20);
|
||||
}
|
||||
|
||||
|
||||
public static String[] getClassPath() {
|
||||
return System.getProperty("java.class.path").split(File.pathSeparator);
|
||||
}
|
||||
|
||||
|
||||
public static URL[] getClassPathURL() {
|
||||
String[] cp = System.getProperty("java.class.path").split(File.pathSeparator);
|
||||
URL[] list = new URL[cp.length];
|
||||
|
||||
for (int i = 0; i < cp.length; i++) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = new URL(cp[i]);
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
list[i] = url;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static X509Certificate[] getCertificates(Class<?> clazz) {
|
||||
Object[] signers = clazz.getSigners();
|
||||
if (signers == null) return null;
|
||||
return Arrays.stream(signers).filter((c) -> c instanceof X509Certificate).map((c) -> (X509Certificate) c).toArray(X509Certificate[]::new);
|
||||
}
|
||||
|
||||
public static void checkStackTrace(Class<?> mainClass) {
|
||||
LogHelper.debug("Testing stacktrace");
|
||||
Exception e = new Exception("Testing stacktrace");
|
||||
StackTraceElement[] list = e.getStackTrace();
|
||||
if (!list[list.length - 1].getClassName().equals(mainClass.getName())) {
|
||||
throw new SecurityException(String.format("Invalid StackTraceElement: %s", list[list.length - 1].getClassName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private static int getCorrectOSArch() {
|
||||
// As always, mustdie must die
|
||||
if (OS_TYPE == OS.MUSTDIE)
|
||||
return System.getenv("ProgramFiles(x86)") == null ? 32 : 64;
|
||||
|
||||
// Or trust system property (maybe incorrect)
|
||||
return System.getProperty("os.arch").contains("64") ? 64 : 32;
|
||||
}
|
||||
|
||||
|
||||
public static String getEnvPropertyCaseSensitive(String name) {
|
||||
return System.getenv().get(name);
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static boolean isJVMMatchesSystemArch() {
|
||||
return JVM_BITS == OS_BITS;
|
||||
}
|
||||
|
||||
|
||||
public static String jvmProperty(String name, String value) {
|
||||
return String.format("-D%s=%s", name, value);
|
||||
}
|
||||
|
||||
|
||||
public static String systemToJvmProperty(String name) {
|
||||
return String.format("-D%s=%s", name, System.getProperties().getProperty(name));
|
||||
}
|
||||
|
||||
|
||||
public static void addSystemPropertyToArgs(Collection<String> args, String name) {
|
||||
String property = System.getProperty(name);
|
||||
if (property != null)
|
||||
args.add(String.format("-D%s=%s", name, property));
|
||||
}
|
||||
|
||||
|
||||
public static void verifySystemProperties(Class<?> mainClass, boolean requireSystem) {
|
||||
Locale.setDefault(Locale.US);
|
||||
// Verify class loader
|
||||
LogHelper.debug("Verifying class loader");
|
||||
if (requireSystem && !mainClass.getClassLoader().equals(LOADER))
|
||||
throw new SecurityException("ClassLoader should be system");
|
||||
|
||||
// Verify system and java architecture
|
||||
LogHelper.debug("Verifying JVM architecture");
|
||||
}
|
||||
|
||||
public enum OS {
|
||||
MUSTDIE("mustdie"), LINUX("linux"), MACOSX("macosx");
|
||||
|
||||
public final String name;
|
||||
|
||||
OS(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static OS byName(String name) {
|
||||
if (name.startsWith("Windows"))
|
||||
return MUSTDIE;
|
||||
if (name.startsWith("Linux"))
|
||||
return LINUX;
|
||||
if (name.startsWith("Mac OS X"))
|
||||
return MACOSX;
|
||||
throw new RuntimeException(String.format("This shit is not yet supported: '%s'", name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -15,29 +15,12 @@
|
|||
}
|
||||
|
||||
sourceSets {
|
||||
java11 {
|
||||
java {
|
||||
srcDirs = ['src/main/java11']
|
||||
}
|
||||
dependencies {
|
||||
java11Implementation project(':LauncherAPI')
|
||||
java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
compileJava11Java {
|
||||
sourceCompatibility = 11
|
||||
targetCompatibility = 11
|
||||
}
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
jar {
|
||||
into('META-INF/versions/11') {
|
||||
from sourceSets.java11.output
|
||||
}
|
||||
archiveClassifier.set('clean')
|
||||
manifest.attributes("Main-Class": mainClassName,
|
||||
"Premain-Class": mainAgentName,
|
||||
|
|
|
@ -1,10 +1,69 @@
|
|||
package pro.gravit.launcher.server.launch;
|
||||
|
||||
import pro.gravit.launcher.server.ServerWrapper;
|
||||
import pro.gravit.utils.PublicURLClassLoader;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
public class ModuleLaunch implements Launch {
|
||||
@Override
|
||||
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
||||
public void run(String mainclass, ServerWrapper.Config config, String[] args) throws Throwable {
|
||||
throw new UnsupportedOperationException("Module system not supported");
|
||||
URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new);
|
||||
ClassLoader ucl = new PublicURLClassLoader(urls);
|
||||
// Create Module Layer
|
||||
ModuleFinder finder = ModuleFinder.of(config.moduleConf.modulePath.stream().map(Paths::get).toArray(Path[]::new));
|
||||
ModuleLayer bootLayer = ModuleLayer.boot();
|
||||
Configuration configuration = bootLayer.configuration()
|
||||
.resolveAndBind(ModuleFinder.of(), finder, config.moduleConf.modules);
|
||||
ModuleLayer.Controller controller = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(bootLayer), ucl);
|
||||
ModuleLayer layer = controller.layer();
|
||||
// Configure exports / opens
|
||||
for(var e : config.moduleConf.exports.entrySet()) {
|
||||
String[] split = e.getKey().split("\\\\");
|
||||
Module source = layer.findModule(split[0]).orElseThrow();
|
||||
String pkg = split[1];
|
||||
Module target = layer.findModule(e.getValue()).orElseThrow();
|
||||
controller.addExports(source, pkg, target);
|
||||
}
|
||||
for(var e : config.moduleConf.opens.entrySet()) {
|
||||
String[] split = e.getKey().split("\\\\");
|
||||
Module source = layer.findModule(split[0]).orElseThrow();
|
||||
String pkg = split[1];
|
||||
Module target = layer.findModule(e.getValue()).orElseThrow();
|
||||
controller.addOpens(source, pkg, target);
|
||||
}
|
||||
for(var e : config.moduleConf.reads.entrySet()) {
|
||||
Module source = layer.findModule(e.getKey()).orElseThrow();
|
||||
Module target = layer.findModule(e.getValue()).orElseThrow();
|
||||
controller.addReads(source, target);
|
||||
}
|
||||
Module mainModule = layer.findModule(config.moduleConf.mainModule).orElseThrow();
|
||||
Module unnamed = ModuleLaunch.class.getClassLoader().getUnnamedModule();
|
||||
if(unnamed != null) {
|
||||
controller.addOpens(mainModule, getPackageFromClass(config.mainclass), unnamed);
|
||||
}
|
||||
// Start main class
|
||||
ClassLoader loader = mainModule.getClassLoader();
|
||||
Class<?> mainClass = Class.forName(mainclass, true, loader);
|
||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
||||
mainMethod.invoke(args);
|
||||
}
|
||||
|
||||
private static String getPackageFromClass(String clazz) {
|
||||
int index = clazz.lastIndexOf(".");
|
||||
if(index >= 0) {
|
||||
return clazz.substring(0, index);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
package pro.gravit.launcher.server.launch;
|
||||
|
||||
import pro.gravit.launcher.server.ServerWrapper;
|
||||
import pro.gravit.utils.PublicURLClassLoader;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
public class ModuleLaunch implements Launch {
|
||||
@Override
|
||||
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
||||
public void run(String mainclass, ServerWrapper.Config config, String[] args) throws Throwable {
|
||||
URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new);
|
||||
ClassLoader ucl = new PublicURLClassLoader(urls);
|
||||
// Create Module Layer
|
||||
ModuleFinder finder = ModuleFinder.of(config.moduleConf.modulePath.stream().map(Paths::get).toArray(Path[]::new));
|
||||
ModuleLayer bootLayer = ModuleLayer.boot();
|
||||
Configuration configuration = bootLayer.configuration()
|
||||
.resolveAndBind(ModuleFinder.of(), finder, config.moduleConf.modules);
|
||||
ModuleLayer.Controller controller = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(bootLayer), ucl);
|
||||
ModuleLayer layer = controller.layer();
|
||||
// Configure exports / opens
|
||||
for(var e : config.moduleConf.exports.entrySet()) {
|
||||
String[] split = e.getKey().split("\\\\");
|
||||
Module source = layer.findModule(split[0]).orElseThrow();
|
||||
String pkg = split[1];
|
||||
Module target = layer.findModule(e.getValue()).orElseThrow();
|
||||
controller.addExports(source, pkg, target);
|
||||
}
|
||||
for(var e : config.moduleConf.opens.entrySet()) {
|
||||
String[] split = e.getKey().split("\\\\");
|
||||
Module source = layer.findModule(split[0]).orElseThrow();
|
||||
String pkg = split[1];
|
||||
Module target = layer.findModule(e.getValue()).orElseThrow();
|
||||
controller.addOpens(source, pkg, target);
|
||||
}
|
||||
for(var e : config.moduleConf.reads.entrySet()) {
|
||||
Module source = layer.findModule(e.getKey()).orElseThrow();
|
||||
Module target = layer.findModule(e.getValue()).orElseThrow();
|
||||
controller.addReads(source, target);
|
||||
}
|
||||
Module mainModule = layer.findModule(config.moduleConf.mainModule).orElseThrow();
|
||||
Module unnamed = ModuleLaunch.class.getClassLoader().getUnnamedModule();
|
||||
if(unnamed != null) {
|
||||
controller.addOpens(mainModule, getPackageFromClass(config.mainclass), unnamed);
|
||||
}
|
||||
// Start main class
|
||||
ClassLoader loader = mainModule.getClassLoader();
|
||||
Class<?> mainClass = Class.forName(mainclass, true, loader);
|
||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
||||
mainMethod.invoke(args);
|
||||
}
|
||||
|
||||
private static String getPackageFromClass(String clazz) {
|
||||
int index = clazz.lastIndexOf(".");
|
||||
if(index >= 0) {
|
||||
return clazz.substring(0, index);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
id 'com.github.johnrengelman.shadow' version '5.2.0' apply false
|
||||
id 'com.github.johnrengelman.shadow' version '7.1.2' apply false
|
||||
id 'maven-publish'
|
||||
id 'signing'
|
||||
id 'org.openjfx.javafxplugin' version '0.0.10' apply false
|
||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
|||
Subproject commit aba8a880bd644c211f6f6d6fdceedd21adf66ca6
|
||||
Subproject commit b20ea06d769fc22dcc47db5a1afcda3bc1e996b5
|
Loading…
Reference in a new issue