diff --git a/src/main/java/org/fusesource/jansi/internal/AnsiConsoleSupport.java b/src/main/java/org/fusesource/jansi/internal/AnsiConsoleSupport.java index 4868207d..64087fa1 100644 --- a/src/main/java/org/fusesource/jansi/internal/AnsiConsoleSupport.java +++ b/src/main/java/org/fusesource/jansi/internal/AnsiConsoleSupport.java @@ -20,9 +20,9 @@ import org.fusesource.jansi.io.AnsiProcessor; -public interface AnsiConsoleSupport { +public abstract class AnsiConsoleSupport { - interface CLibrary { + public interface CLibrary { int STDOUT_FILENO = 1; int STDERR_FILENO = 2; @@ -32,7 +32,7 @@ interface CLibrary { int isTty(int fd); } - interface Kernel32 { + public interface Kernel32 { int isTty(long console); @@ -51,9 +51,39 @@ interface Kernel32 { AnsiProcessor newProcessor(OutputStream os, long console) throws IOException; } - String getProviderName(); + private final String providerName; + private CLibrary cLibrary; + private Kernel32 kernel32; - CLibrary getCLibrary(); + protected AnsiConsoleSupport(String providerName) { + this.providerName = providerName; + } + + public final String getProviderName() { + return providerName; + } + + protected abstract CLibrary createCLibrary(); + + protected abstract Kernel32 createKernel32(); + + public final CLibrary getCLibrary() { + if (cLibrary == null) { + cLibrary = createCLibrary(); + } - Kernel32 getKernel32(); + return cLibrary; + } + + public final Kernel32 getKernel32() { + if (kernel32 == null) { + if (!OSInfo.isWindows()) { + throw new RuntimeException("Kernel32 is not available on this platform"); + } + + kernel32 = createKernel32(); + } + + return kernel32; + } } diff --git a/src/main/java/org/fusesource/jansi/internal/AnsiConsoleSupportHolder.java b/src/main/java/org/fusesource/jansi/internal/AnsiConsoleSupportHolder.java index bfb0c613..9e66e579 100644 --- a/src/main/java/org/fusesource/jansi/internal/AnsiConsoleSupportHolder.java +++ b/src/main/java/org/fusesource/jansi/internal/AnsiConsoleSupportHolder.java @@ -86,54 +86,22 @@ private static AnsiConsoleSupport findProvider(String providerList) { ERR = err; } - public static String getProviderName() { - return PROVIDER.getProviderName(); + public static AnsiConsoleSupport getProvider() { + if (PROVIDER == null) { + throw new RuntimeException("No provider available", ERR); + } + return PROVIDER; } - private static final class LibraryHolder { - static final AnsiConsoleSupport.CLibrary CLIBRARY; - static final AnsiConsoleSupport.Kernel32 KERNEL32; - static final Throwable ERR; - - static { - AnsiConsoleSupport.CLibrary clib = null; - AnsiConsoleSupport.Kernel32 kernel32 = null; - Throwable err = null; - - if (PROVIDER != null) { - try { - clib = PROVIDER.getCLibrary(); - kernel32 = OSInfo.isWindows() ? PROVIDER.getKernel32() : null; - } catch (Throwable e) { - err = e; - } - } else { - err = AnsiConsoleSupportHolder.ERR; - } - - CLIBRARY = clib; - KERNEL32 = kernel32; - ERR = err; - } + public static String getProviderName() { + return getProvider().getProviderName(); } public static AnsiConsoleSupport.CLibrary getCLibrary() { - if (LibraryHolder.CLIBRARY == null) { - throw new RuntimeException("Unable to get the instance of CLibrary", LibraryHolder.ERR); - } - - return LibraryHolder.CLIBRARY; + return getProvider().getCLibrary(); } public static AnsiConsoleSupport.Kernel32 getKernel32() { - if (LibraryHolder.KERNEL32 == null) { - if (OSInfo.isWindows()) { - throw new RuntimeException("Unable to get the instance of Kernel32", LibraryHolder.ERR); - } else { - throw new UnsupportedOperationException("Not Windows"); - } - } - - return LibraryHolder.KERNEL32; + return getProvider().getKernel32(); } } diff --git a/src/main/java/org/fusesource/jansi/internal/NativeImageFeature.java b/src/main/java/org/fusesource/jansi/internal/NativeImageFeature.java index 0229530a..27063a11 100644 --- a/src/main/java/org/fusesource/jansi/internal/NativeImageFeature.java +++ b/src/main/java/org/fusesource/jansi/internal/NativeImageFeature.java @@ -15,6 +15,8 @@ */ package org.fusesource.jansi.internal; +import java.util.Objects; + import org.fusesource.jansi.AnsiConsole; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; @@ -28,6 +30,8 @@ public String getURL() { @Override public void duringSetup(DuringSetupAccess access) { + RuntimeClassInitialization.initializeAtBuildTime(AnsiConsoleSupportHolder.class); + String providers = System.getProperty(AnsiConsole.JANSI_PROVIDERS); if (providers != null) { try { @@ -38,18 +42,8 @@ public void duringSetup(DuringSetupAccess access) { } } - if (providers != null && providers.contains(AnsiConsole.JANSI_PROVIDER_FFM)) { - try { - // FFM is only available in JDK 21+, so we need to compile it separately - Class.forName("org.fusesource.jansi.internal.ffm.NativeImageDowncallRegister") - .getMethod("registerForDowncall") - .invoke(null); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - if (providers == null || providers.contains(AnsiConsole.JANSI_PROVIDER_JNI)) { + String provider = Objects.requireNonNull(AnsiConsoleSupportHolder.getProviderName(), "No provider available"); + if (provider.equals(AnsiConsole.JANSI_PROVIDER_JNI)) { String jansiNativeLibraryName = System.mapLibraryName("jansi"); if (jansiNativeLibraryName.endsWith(".dylib")) { jansiNativeLibraryName = jansiNativeLibraryName.replace(".dylib", ".jnilib"); @@ -76,8 +70,15 @@ public void duringSetup(DuringSetupAccess access) { // GraalVM version < 22.3 // Users need to manually add the JNI library as resources } + } else if (provider.equals(AnsiConsole.JANSI_PROVIDER_FFM)) { + try { + // FFM is only available in JDK 21+, so we need to compile it separately + Class.forName("org.fusesource.jansi.internal.ffm.NativeImageDowncallRegister") + .getMethod("registerForDowncall") + .invoke(null); + } catch (Throwable e) { + throw new RuntimeException(e); + } } - - RuntimeClassInitialization.initializeAtBuildTime(AnsiConsoleSupportHolder.class); } } diff --git a/src/main/java/org/fusesource/jansi/internal/ffm/AnsiConsoleSupportImpl.java b/src/main/java/org/fusesource/jansi/internal/ffm/AnsiConsoleSupportImpl.java index f22f3ac7..b36bc692 100644 --- a/src/main/java/org/fusesource/jansi/internal/ffm/AnsiConsoleSupportImpl.java +++ b/src/main/java/org/fusesource/jansi/internal/ffm/AnsiConsoleSupportImpl.java @@ -27,11 +27,14 @@ import static org.fusesource.jansi.internal.ffm.Kernel32.*; -public final class AnsiConsoleSupportImpl implements AnsiConsoleSupport { +public final class AnsiConsoleSupportImpl extends AnsiConsoleSupport { - public AnsiConsoleSupportImpl() {} + public AnsiConsoleSupportImpl() { + super("ffm"); + } public AnsiConsoleSupportImpl(boolean checkNativeAccess) { + this(); if (checkNativeAccess && !AnsiConsoleSupportImpl.class.getModule().isNativeAccessEnabled()) { throw new UnsupportedOperationException( "Native access is not enabled for the current module: " + AnsiConsoleSupportImpl.class.getModule()); @@ -39,12 +42,7 @@ public AnsiConsoleSupportImpl(boolean checkNativeAccess) { } @Override - public String getProviderName() { - return "ffm"; - } - - @Override - public CLibrary getCLibrary() { + protected CLibrary createCLibrary() { if (OSInfo.isWindows()) { return new WindowsCLibrary(); } else { @@ -53,7 +51,7 @@ public CLibrary getCLibrary() { } @Override - public Kernel32 getKernel32() { + protected Kernel32 createKernel32() { return new Kernel32() { @Override public int isTty(long console) { diff --git a/src/main/java/org/fusesource/jansi/internal/jni/AnsiConsoleSupportImpl.java b/src/main/java/org/fusesource/jansi/internal/jni/AnsiConsoleSupportImpl.java index ea3bc2fc..e3ae4d67 100644 --- a/src/main/java/org/fusesource/jansi/internal/jni/AnsiConsoleSupportImpl.java +++ b/src/main/java/org/fusesource/jansi/internal/jni/AnsiConsoleSupportImpl.java @@ -33,15 +33,14 @@ import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE; import static org.fusesource.jansi.internal.Kernel32.SetConsoleMode; -public final class AnsiConsoleSupportImpl implements AnsiConsoleSupport { +public final class AnsiConsoleSupportImpl extends AnsiConsoleSupport { - @Override - public String getProviderName() { - return "jni"; + public AnsiConsoleSupportImpl() { + super("jni"); } @Override - public CLibrary getCLibrary() { + protected CLibrary createCLibrary() { return new CLibrary() { @Override public short getTerminalWidth(int fd) { @@ -56,7 +55,7 @@ public int isTty(int fd) { } @Override - public Kernel32 getKernel32() { + protected Kernel32 createKernel32() { return new Kernel32() { @Override public int isTty(long console) {