Use reflection to access DatatypeConverter

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
TheKodeToad 2022-11-22 16:56:58 +00:00
parent bfa5fe1598
commit 8a6776731a
3 changed files with 52 additions and 10 deletions

View File

@ -40,6 +40,7 @@ import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory; import java.net.URLStreamHandlerFactory;
import org.prismlauncher.fix.Fix; import org.prismlauncher.fix.Fix;
import org.prismlauncher.utils.Base64;
import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.Parameters;
import org.prismlauncher.utils.logging.Log; import org.prismlauncher.utils.logging.Log;
import org.prismlauncher.utils.url.UrlUtils; import org.prismlauncher.utils.url.UrlUtils;
@ -59,7 +60,7 @@ public final class SkinFix implements Fix, URLStreamHandlerFactory {
@Override @Override
public boolean isApplicable(Parameters params) { public boolean isApplicable(Parameters params) {
if (!UrlUtils.isSupported()) { if (!UrlUtils.isSupported() || !Base64.isSupported()) {
Log.warning("Cannot access the necessary Java internals for skin fix"); Log.warning("Cannot access the necessary Java internals for skin fix");
Log.warning("Try adding '--add-opens java.base/java.net=ALL-UNNAMED' to your Java arguments"); Log.warning("Try adding '--add-opens java.base/java.net=ALL-UNNAMED' to your Java arguments");
Log.warning("Alternatively, turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); Log.warning("Alternatively, turning off legacy skin fix in Settings > Miscellaneous will silence the warnings");

View File

@ -35,25 +35,60 @@
package org.prismlauncher.utils; package org.prismlauncher.utils;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import javax.xml.bind.DatatypeConverter; import org.prismlauncher.utils.logging.Log;
/**
* Uses Base64 with Java 8 or later, otherwise DatatypeConverter. In the latter
* case, reflection is used to allow using newer compilers.
*/
public final class Base64 { public final class Base64 {
private static boolean legacy; private static boolean supported = true;
private static MethodHandle legacy;
public static byte[] decode(String input) { static {
if (!legacy) { try {
Class.forName("java.util.Base64");
} catch (ClassNotFoundException e) {
try { try {
return java.util.Base64.getDecoder().decode(input.getBytes(StandardCharsets.UTF_8)); Class<?> datatypeConverter = Class.forName("javax.xml.bind.DatatypeConverter");
} catch (NoClassDefFoundError e) { legacy = MethodHandles.lookup().findStatic(datatypeConverter, "parseBase64Binary",
legacy = true; MethodType.methodType(byte[].class, String.class));
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e1) {
Log.error("Base64 not supported", e1);
supported = false;
} }
} }
}
// support for Java versions < 8 /**
return DatatypeConverter.parseBase64Binary(input); * Determines whether base64 is supported.
*
* @return <code>true</code> if base64 can be parsed
*/
public static boolean isSupported() {
return supported;
}
public static byte[] decode(String input) {
if (!isSupported())
throw new UnsupportedOperationException();
if (legacy == null)
return java.util.Base64.getDecoder().decode(input.getBytes(StandardCharsets.UTF_8));
try {
return (byte[]) legacy.invokeExact(input);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new Error(e);
}
} }
} }

View File

@ -82,10 +82,16 @@ public final class UrlUtils {
} }
public static URLConnection openHttpConnection(URL url, Proxy proxy) throws IOException { public static URLConnection openHttpConnection(URL url, Proxy proxy) throws IOException {
if (http == null)
throw new UnsupportedOperationException();
return openConnection(http, url, proxy); return openConnection(http, url, proxy);
} }
public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException { public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException {
if (openConnection == null)
throw new UnsupportedOperationException();
try { try {
return (URLConnection) openConnection.invokeExact(handler, url, proxy); return (URLConnection) openConnection.invokeExact(handler, url, proxy);
} catch (IOException | Error | RuntimeException e) { } catch (IOException | Error | RuntimeException e) {