From ec82d700bbdc0de6e1c268d9dbe470274ac481c6 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Tue, 6 Feb 2024 19:58:06 +0100 Subject: [PATCH 01/15] Avoid attempt to make java.awt.GraphicsEnvironment non final, as this breaks in Java 18+. Instead, use ByteBuddy to return CTCGraphicsEnvironment when sun.awt.PlatformGraphicsInfo.createGE() is called. --- .../cacio/ctc/junit/CacioExtension.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java index 8974a41..0a94e3e 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java @@ -26,6 +26,9 @@ import com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment; import com.github.caciocavallosilano.cacio.ctc.CTCToolkit; +import org.assertj.core.internal.bytebuddy.ByteBuddy; +import org.assertj.core.internal.bytebuddy.implementation.FixedValue; +import org.assertj.core.internal.bytebuddy.matcher.ElementMatchers; import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; @@ -65,20 +68,16 @@ public class CacioExtension implements ExecutionCondition { headlessField.setAccessible(true); headlessField.set(null, Boolean.TRUE); - Class geCls = Class.forName("java.awt.GraphicsEnvironment$LocalGE"); - Field ge = geCls.getDeclaredField("INSTANCE"); - ge.setAccessible(true); + injectCTCGraphicsEnvironment(); + defaultHeadlessField.set(null, Boolean.FALSE); headlessField.set(null, Boolean.FALSE); - makeNonFinal(ge); - Class smfCls = Class.forName("sun.java2d.SurfaceManagerFactory"); Field smf = smfCls.getDeclaredField("instance"); smf.setAccessible(true); smf.set(null, null); - ge.set(null, new CTCGraphicsEnvironment()); } catch (Exception e) { e.printStackTrace(); } @@ -86,11 +85,14 @@ public class CacioExtension implements ExecutionCondition { System.setProperty("swing.defaultlaf", MetalLookAndFeel.class.getName()); } - public static void makeNonFinal(Field field) { - int mods = field.getModifiers(); - if (Modifier.isFinal(mods)) { - MODIFIERS.set(field, mods & ~Modifier.FINAL); - } + public static void injectCTCGraphicsEnvironment() throws ClassNotFoundException { + new ByteBuddy() + .subclass(Class.forName("sun.awt.PlatformGraphicsInfo")) + .method(ElementMatchers.named("createGE")) + .intercept(FixedValue.value(new CTCGraphicsEnvironment())) + .make() + .load(CacioExtension.class.getClassLoader()) + .getLoaded(); } @Override From 6132e1a53985dfd8d80255786b500d2de4a3d0ff Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Tue, 6 Feb 2024 19:59:00 +0100 Subject: [PATCH 02/15] Ignore jenv file, and log files --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index 3034361..7d5b006 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,12 @@ target/ # OS X .DS_Store + +# jenv + + +# jenv +.java-version + +# log files +*.log From 85c7054dbd6fb38caa856069e3c79ab1753f127d Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Wed, 7 Feb 2024 09:43:40 +0100 Subject: [PATCH 03/15] Ignore jenv and log files --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index 3034361..7d5b006 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,12 @@ target/ # OS X .DS_Store + +# jenv + + +# jenv +.java-version + +# log files +*.log From 8ef7cad23c2948a39f7ae1b402fed24055e3e8a0 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Wed, 7 Feb 2024 09:46:10 +0100 Subject: [PATCH 04/15] Fix UnsupportedOperationException: inject CTCGraphicsEnvironment through sun.awt.PlatformGraphicsInfo.createGE() --- .../cacio/ctc/junit/CacioExtension.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java index 8974a41..ab0adce 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java @@ -26,6 +26,9 @@ import com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment; import com.github.caciocavallosilano.cacio.ctc.CTCToolkit; +import org.assertj.core.internal.bytebuddy.ByteBuddy; +import org.assertj.core.internal.bytebuddy.implementation.FixedValue; +import org.assertj.core.internal.bytebuddy.matcher.ElementMatchers; import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; @@ -65,20 +68,16 @@ public class CacioExtension implements ExecutionCondition { headlessField.setAccessible(true); headlessField.set(null, Boolean.TRUE); - Class geCls = Class.forName("java.awt.GraphicsEnvironment$LocalGE"); - Field ge = geCls.getDeclaredField("INSTANCE"); - ge.setAccessible(true); + injectCTCGraphicsEnvironment(); + defaultHeadlessField.set(null, Boolean.FALSE); headlessField.set(null, Boolean.FALSE); - makeNonFinal(ge); - Class smfCls = Class.forName("sun.java2d.SurfaceManagerFactory"); Field smf = smfCls.getDeclaredField("instance"); smf.setAccessible(true); smf.set(null, null); - ge.set(null, new CTCGraphicsEnvironment()); } catch (Exception e) { e.printStackTrace(); } @@ -86,11 +85,14 @@ public class CacioExtension implements ExecutionCondition { System.setProperty("swing.defaultlaf", MetalLookAndFeel.class.getName()); } - public static void makeNonFinal(Field field) { - int mods = field.getModifiers(); - if (Modifier.isFinal(mods)) { - MODIFIERS.set(field, mods & ~Modifier.FINAL); - } + private static void injectCTCGraphicsEnvironment() throws ClassNotFoundException { + new ByteBuddy() + .subclass(Class.forName("sun.awt.PlatformGraphicsInfo")) + .method(ElementMatchers.named("createGE")) + .intercept(FixedValue.value(new CTCGraphicsEnvironment())) + .make() + .load(CacioExtension.class.getClassLoader()) + .getLoaded(); } @Override From 8d09a9de9932d9026dc6e66e4ea7705c558a9a3a Mon Sep 17 00:00:00 2001 From: janblom Date: Wed, 7 Feb 2024 10:29:06 +0100 Subject: [PATCH 05/15] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3e7072c..227bdeb 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ [![License](https://img.shields.io/github/license/CaciocavalloSilano/caciocavallo.svg)](https://raw.githubusercontent.com/CaciocavalloSilano/caciocavallo/master/LICENSE) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.caciocavallosilano/cacio-tta/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.caciocavallosilano/cacio-tta) +## Please note +This is a fork of the Caciocavallo project. It's purpose is to enable using a fixed version in the OHDSI Rebbit tools (for Java 18+) until this fix is available in the +parent project. + ## Introduction One problem with running GUI tests is that they need to create windows, grab keyboard focus, and do all sorts of interaction with the screen. From b759f928c10d5f426ecc2771df494960d371855c Mon Sep 17 00:00:00 2001 From: janblom Date: Wed, 7 Feb 2024 10:31:41 +0100 Subject: [PATCH 06/15] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 227bdeb..ffbe64e 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.caciocavallosilano/cacio-tta/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.caciocavallosilano/cacio-tta) ## Please note -This is a fork of the Caciocavallo project. It's purpose is to enable using a fixed version in the OHDSI Rebbit tools (for Java 18+) until this fix is available in the -parent project. +This is a fork of the Caciocavallo project. It's purpose is to enable using a fixed version in the OHDSI Rabbit tools (for Java 18+) until this fix is available in the +parent project. An issue for this has been created in the parent project, with an offer to create a PR. (Jan Blom) ## Introduction From 39c52ced103234db042fc63567709ffd0709a471 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Thu, 8 Feb 2024 17:59:56 +0100 Subject: [PATCH 07/15] Replace removing a final modifier, which is not possible in JDK 18+, by ByteBuddy interceptors. Needed to change PlatformWindowFactory from an interface into a plain class, because they are needed in the boostrap class loader (interfaces and abstract classes cannot be injected). Some other classes need to be public for the same reason. --- .../cacio/peer/PlatformWindowFactory.java | 26 +++-- .../peer/managed/FullScreenWindowFactory.java | 17 ++- cacio-tta/pom.xml | 11 ++ .../cacio/ctc/CTCSurfaceManagerFactory.java | 2 +- .../cacio/ctc/CTCVolatileSurfaceManager.java | 2 +- .../cacio/ctc/junit/CTCInterceptor.java | 29 +++++ .../cacio/ctc/junit/CacioExtension.java | 110 ++++++++++++++---- pom.xml | 1 + 8 files changed, 160 insertions(+), 38 deletions(-) create mode 100644 cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java diff --git a/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/PlatformWindowFactory.java b/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/PlatformWindowFactory.java index f69725e..7ad4cd1 100644 --- a/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/PlatformWindowFactory.java +++ b/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/PlatformWindowFactory.java @@ -28,7 +28,7 @@ /** * Creates {@link PlatformWindow} instances for use by the component peers. */ -public interface PlatformWindowFactory { +public class PlatformWindowFactory { /** * Creates a {@link PlatformWindow} instance. @@ -38,8 +38,10 @@ public interface PlatformWindowFactory { * * @return the platform window instance */ - PlatformWindow createPlatformWindow(CacioComponent awtComponent, - PlatformWindow parent); + protected PlatformWindow createPlatformWindow(CacioComponent awtComponent, + PlatformWindow parent) { + return null; + } /** * Creates and returns a toplevel window for the specified peer. @@ -48,7 +50,9 @@ PlatformWindow createPlatformWindow(CacioComponent awtComponent, * * @return the created toplevel window */ - PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent component); + public PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent component) { + return null; + } /** * Creates and returns the event pump to be used for getting the platform @@ -56,7 +60,9 @@ PlatformWindow createPlatformWindow(CacioComponent awtComponent, * * @return the event source for the toolkit */ - CacioEventPump createEventPump(); + public CacioEventPump createEventPump() { + return null; + } /** * Creates and returns a toplevel window with the specified window as @@ -67,6 +73,12 @@ PlatformWindow createPlatformWindow(CacioComponent awtComponent, * @return the created toplevel window */ public PlatformWindow createPlatformToplevelWindow(CacioComponent component, - PlatformWindow owner); + PlatformWindow owner) { + return null; + } -} +// public PlatformWindow createPlatformToplevelWindow(CacioComponent component, +// PlatformWindow notUsed) { + + + } diff --git a/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/managed/FullScreenWindowFactory.java b/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/managed/FullScreenWindowFactory.java index 3478a58..4676137 100644 --- a/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/managed/FullScreenWindowFactory.java +++ b/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/managed/FullScreenWindowFactory.java @@ -40,7 +40,7 @@ import com.github.caciocavallosilano.cacio.peer.PlatformWindowFactory; import sun.security.action.GetPropertyAction; -public class FullScreenWindowFactory implements PlatformWindowFactory { +public class FullScreenWindowFactory extends PlatformWindowFactory { private static final Dimension screenSize; static { @@ -133,7 +133,7 @@ public FullScreenWindowFactory(PlatformScreenSelector screenSelector, * * @return the platform window instance */ - public final PlatformWindow createPlatformWindow(CacioComponent awtComponent, + public PlatformWindow createPlatformWindow(CacioComponent awtComponent, PlatformWindow parent) { if (parent == null) { throw new IllegalArgumentException("parent cannot be null"); @@ -143,17 +143,16 @@ public final PlatformWindow createPlatformWindow(CacioComponent awtComponent, return new ManagedWindow(p, awtComponent); } - @Override - public final - PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent comp) { - PlatformScreen screen = selector.getPlatformScreen(comp); + //@Override abstract PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent component) + public PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent component) { + PlatformScreen screen = selector.getPlatformScreen(component); ScreenManagedWindowContainer smwc = screenMap.get(screen); if (smwc == null) { smwc = new ScreenManagedWindowContainer(screen); screenMap.put(screen, smwc); } - return new ManagedWindow(smwc, comp); + return new ManagedWindow(smwc, component); } /** @@ -161,14 +160,14 @@ PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent comp) { * Note: owners are currently ignored in fully managed * windows. */ - @Override + //@Override public PlatformWindow createPlatformToplevelWindow(CacioComponent component, PlatformWindow notUsed) { return createPlatformToplevelWindow(component); } - @Override + //@Override public CacioEventPump createEventPump() { FullScreenEventSource s = new FullScreenEventSource(); return new FullScreenEventPump(s); diff --git a/cacio-tta/pom.xml b/cacio-tta/pom.xml index d1067c2..bbadf41 100644 --- a/cacio-tta/pom.xml +++ b/cacio-tta/pom.xml @@ -45,6 +45,17 @@ jide-oss 3.6.18 + + + net.bytebuddy + byte-buddy + 1.14.11 + + + net.bytebuddy + byte-buddy-agent + 1.14.11 + diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCSurfaceManagerFactory.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCSurfaceManagerFactory.java index b06387c..7bc20a4 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCSurfaceManagerFactory.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCSurfaceManagerFactory.java @@ -28,7 +28,7 @@ import sun.awt.image.VolatileSurfaceManager; import sun.java2d.SurfaceManagerFactory; -class CTCSurfaceManagerFactory extends SurfaceManagerFactory { +public class CTCSurfaceManagerFactory extends SurfaceManagerFactory { @Override public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCVolatileSurfaceManager.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCVolatileSurfaceManager.java index 236dc57..81e36bf 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCVolatileSurfaceManager.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCVolatileSurfaceManager.java @@ -28,7 +28,7 @@ import sun.awt.image.VolatileSurfaceManager; import sun.java2d.SurfaceData; -class CTCVolatileSurfaceManager extends VolatileSurfaceManager { +public class CTCVolatileSurfaceManager extends VolatileSurfaceManager { protected CTCVolatileSurfaceManager(SunVolatileImage vImg, Object context) { super(vImg, context); diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java new file mode 100644 index 0000000..fd828e3 --- /dev/null +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java @@ -0,0 +1,29 @@ +package com.github.caciocavallosilano.cacio.ctc.junit; + +import com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.implementation.bind.annotation.*; +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.awt.*; +import java.lang.reflect.Method; + + +public class CTCInterceptor { + private static CTCGraphicsEnvironment ctcGraphicsEnvironment = new CTCGraphicsEnvironment(); + + @RuntimeType + public static GraphicsEnvironment intercept1(@This Object self, + @Origin Method method, + @AllArguments Object[] args, + @SuperMethod Method superMethod) { + return ctcGraphicsEnvironment; + } + + @RuntimeType + public static GraphicsEnvironment intercept2() { + return ctcGraphicsEnvironment; + } +} diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java index 0a94e3e..09380ab 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java @@ -24,11 +24,19 @@ */ package com.github.caciocavallosilano.cacio.ctc.junit; -import com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment; -import com.github.caciocavallosilano.cacio.ctc.CTCToolkit; -import org.assertj.core.internal.bytebuddy.ByteBuddy; -import org.assertj.core.internal.bytebuddy.implementation.FixedValue; -import org.assertj.core.internal.bytebuddy.matcher.ElementMatchers; +import com.github.caciocavallosilano.cacio.ctc.*; +import com.github.caciocavallosilano.cacio.peer.PlatformWindowFactory; +import com.github.caciocavallosilano.cacio.peer.managed.FullScreenWindowFactory; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; +import net.bytebuddy.dynamic.loading.ClassInjector; +import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.*; +import net.bytebuddy.matcher.ElementMatchers; +import net.bytebuddy.pool.TypePool; +import net.bytebuddy.agent.ByteBuddyAgent; import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; @@ -36,11 +44,17 @@ import javax.swing.plaf.metal.MetalLookAndFeel; import java.awt.*; +import java.io.IOException; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; +import java.lang.reflect.Method; +import java.util.Map; + + +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; + public class CacioExtension implements ExecutionCondition { // https://stackoverflow.com/a/56043252/1050369 @@ -48,6 +62,8 @@ public class CacioExtension implements ExecutionCondition { static { try { + ByteBuddyAgent.install(); + var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); MODIFIERS = lookup.findVarHandle(Field.class, "modifiers", int.class); } catch (IllegalAccessException | NoSuchFieldException ex) { @@ -57,6 +73,8 @@ public class CacioExtension implements ExecutionCondition { static { try { + injectCTCGraphicsEnvironment(); + Field toolkit = Toolkit.class.getDeclaredField("toolkit"); toolkit.setAccessible(true); toolkit.set(null, new CTCToolkit()); @@ -68,16 +86,9 @@ public class CacioExtension implements ExecutionCondition { headlessField.setAccessible(true); headlessField.set(null, Boolean.TRUE); - injectCTCGraphicsEnvironment(); - defaultHeadlessField.set(null, Boolean.FALSE); headlessField.set(null, Boolean.FALSE); - Class smfCls = Class.forName("sun.java2d.SurfaceManagerFactory"); - Field smf = smfCls.getDeclaredField("instance"); - smf.setAccessible(true); - smf.set(null, null); - } catch (Exception e) { e.printStackTrace(); } @@ -85,14 +96,65 @@ public class CacioExtension implements ExecutionCondition { System.setProperty("swing.defaultlaf", MetalLookAndFeel.class.getName()); } - public static void injectCTCGraphicsEnvironment() throws ClassNotFoundException { - new ByteBuddy() - .subclass(Class.forName("sun.awt.PlatformGraphicsInfo")) - .method(ElementMatchers.named("createGE")) - .intercept(FixedValue.value(new CTCGraphicsEnvironment())) + public static void injectCTCGraphicsEnvironment() throws ClassNotFoundException, IOException { + /* + * ByteBuddy is used to intercept the methods that return the graphics environment in use + * (java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment() and + * sun.awt.PlatformGraphicsInfo.createGE()) + * + * Since java.awt.GraphicsEnvironment is loaded by the bootstrap class loader, + * all classes used by CTCGraphicsEnvironment also need to be available to the bootstrap class loader, + * as that class loader also loads the CTCInterceptor class, which will instantiate CTCGraphicsEnvironment. + */ + injectClassIntoBootstrapClassLoader( + CTCInterceptor.class, + CTCGraphicsEnvironment.class, + CTCSurfaceManagerFactory.class, + CTCGraphicsConfiguration.class, + PlatformWindowFactory.class, + FullScreenWindowFactory.class, + CTCGraphicsDevice.class, + CTCVolatileSurfaceManager.class); + + ByteBuddy byteBuddy = new ByteBuddy(); + + byteBuddy + .redefine( + TypePool.Default.ofSystemLoader().describe("java.awt.GraphicsEnvironment").resolve(), + ClassFileLocator.ForClassLoader.ofSystemLoader()) + .method(ElementMatchers.named("getLocalGraphicsEnvironment")) + .intercept( + MethodDelegation.to(CTCInterceptor.class)) .make() - .load(CacioExtension.class.getClassLoader()) - .getLoaded(); + .load( + Object.class.getClassLoader(), + ClassReloadingStrategy.fromInstalledAgent()); + + TypeDescription platformGraphicInfosType; + platformGraphicInfosType = TypePool.Default.ofSystemLoader().describe("sun.awt.PlatformGraphicsInfo").resolve(); + ClassFileLocator locator = ClassFileLocator.ForClassLoader.ofSystemLoader(); + + byteBuddy + .redefine( + platformGraphicInfosType, + locator) + .method( + nameStartsWith("createGE")) + .intercept( + MethodDelegation.to(GraphicsEnvironmentInterceptor.class)) + .make() + .load( + Thread.currentThread().getContextClassLoader(), + ClassReloadingStrategy.fromInstalledAgent()); + + } + + public static class GraphicsEnvironmentInterceptor { + private static CTCGraphicsEnvironment ctcGraphicsEnvironment = new CTCGraphicsEnvironment(); + @RuntimeType + public static Object intercept(@Origin Method method, @AllArguments final Object[] args) throws Exception { + return ctcGraphicsEnvironment; + } } @Override @@ -102,4 +164,12 @@ public final ConditionEvaluationResult evaluateExecutionCondition(ExtensionConte .map(annotation -> ConditionEvaluationResult.enabled("@GUITest is present")) .orElse(ConditionEvaluationResult.enabled("@GUITest is not present")); } + + private static void injectClassIntoBootstrapClassLoader(Class... classes) throws IOException { + for (Class clazz: classes) { + final byte[] buffer = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/').concat(".class")).readAllBytes(); + ClassInjector.UsingUnsafe injector = new ClassInjector.UsingUnsafe(null); + injector.injectRaw(Map.of(clazz.getName(), buffer)); + } + } } diff --git a/pom.xml b/pom.xml index 6319fa8..62329ae 100644 --- a/pom.xml +++ b/pom.xml @@ -130,6 +130,7 @@ false + -XX:+EnableDynamicAgentLoading --add-exports=java.desktop/java.awt=ALL-UNNAMED --add-exports=java.desktop/java.awt.peer=ALL-UNNAMED --add-exports=java.desktop/sun.awt.image=ALL-UNNAMED From db6dadf6b7e47b90b6f319e365cbf0364bb6a108 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Thu, 8 Feb 2024 23:19:38 +0100 Subject: [PATCH 08/15] Revert changes to the *WindowFactory classes, not needed --- .../cacio/peer/PlatformWindowFactory.java | 26 +++++-------------- .../peer/managed/FullScreenWindowFactory.java | 17 ++++++------ 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/PlatformWindowFactory.java b/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/PlatformWindowFactory.java index 7ad4cd1..f69725e 100644 --- a/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/PlatformWindowFactory.java +++ b/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/PlatformWindowFactory.java @@ -28,7 +28,7 @@ /** * Creates {@link PlatformWindow} instances for use by the component peers. */ -public class PlatformWindowFactory { +public interface PlatformWindowFactory { /** * Creates a {@link PlatformWindow} instance. @@ -38,10 +38,8 @@ public class PlatformWindowFactory { * * @return the platform window instance */ - protected PlatformWindow createPlatformWindow(CacioComponent awtComponent, - PlatformWindow parent) { - return null; - } + PlatformWindow createPlatformWindow(CacioComponent awtComponent, + PlatformWindow parent); /** * Creates and returns a toplevel window for the specified peer. @@ -50,9 +48,7 @@ protected PlatformWindow createPlatformWindow(CacioComponent awtComponent, * * @return the created toplevel window */ - public PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent component) { - return null; - } + PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent component); /** * Creates and returns the event pump to be used for getting the platform @@ -60,9 +56,7 @@ public PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent compon * * @return the event source for the toolkit */ - public CacioEventPump createEventPump() { - return null; - } + CacioEventPump createEventPump(); /** * Creates and returns a toplevel window with the specified window as @@ -73,12 +67,6 @@ public CacioEventPump createEventPump() { * @return the created toplevel window */ public PlatformWindow createPlatformToplevelWindow(CacioComponent component, - PlatformWindow owner) { - return null; - } + PlatformWindow owner); -// public PlatformWindow createPlatformToplevelWindow(CacioComponent component, -// PlatformWindow notUsed) { - - - } +} diff --git a/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/managed/FullScreenWindowFactory.java b/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/managed/FullScreenWindowFactory.java index 4676137..3478a58 100644 --- a/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/managed/FullScreenWindowFactory.java +++ b/cacio-shared/src/main/java/com/github/caciocavallosilano/cacio/peer/managed/FullScreenWindowFactory.java @@ -40,7 +40,7 @@ import com.github.caciocavallosilano.cacio.peer.PlatformWindowFactory; import sun.security.action.GetPropertyAction; -public class FullScreenWindowFactory extends PlatformWindowFactory { +public class FullScreenWindowFactory implements PlatformWindowFactory { private static final Dimension screenSize; static { @@ -133,7 +133,7 @@ public FullScreenWindowFactory(PlatformScreenSelector screenSelector, * * @return the platform window instance */ - public PlatformWindow createPlatformWindow(CacioComponent awtComponent, + public final PlatformWindow createPlatformWindow(CacioComponent awtComponent, PlatformWindow parent) { if (parent == null) { throw new IllegalArgumentException("parent cannot be null"); @@ -143,16 +143,17 @@ public PlatformWindow createPlatformWindow(CacioComponent awtComponent, return new ManagedWindow(p, awtComponent); } - //@Override abstract PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent component) - public PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent component) { - PlatformScreen screen = selector.getPlatformScreen(component); + @Override + public final + PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent comp) { + PlatformScreen screen = selector.getPlatformScreen(comp); ScreenManagedWindowContainer smwc = screenMap.get(screen); if (smwc == null) { smwc = new ScreenManagedWindowContainer(screen); screenMap.put(screen, smwc); } - return new ManagedWindow(smwc, component); + return new ManagedWindow(smwc, comp); } /** @@ -160,14 +161,14 @@ public PlatformToplevelWindow createPlatformToplevelWindow(CacioComponent compon * Note: owners are currently ignored in fully managed * windows. */ - //@Override + @Override public PlatformWindow createPlatformToplevelWindow(CacioComponent component, PlatformWindow notUsed) { return createPlatformToplevelWindow(component); } - //@Override + @Override public CacioEventPump createEventPump() { FullScreenEventSource s = new FullScreenEventSource(); return new FullScreenEventPump(s); From 0c7c036c10c3ac2ef243a7f13ecc7ec9a71b5230 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Fri, 9 Feb 2024 11:17:12 +0100 Subject: [PATCH 09/15] Ensure that there is only one instance of CTCGraphicsEnvironment in use --- .../cacio/ctc/CTCGraphicsEnvironment.java | 6 +++++- .../caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java | 2 +- .../caciocavallosilano/cacio/ctc/junit/CacioExtension.java | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java index 24e8c8c..07c7abe 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java @@ -31,10 +31,14 @@ public class CTCGraphicsEnvironment extends SunGraphicsEnvironment { - public CTCGraphicsEnvironment() { + private static final CTCGraphicsEnvironment INSTANCE = new CTCGraphicsEnvironment(); + private CTCGraphicsEnvironment() { SurfaceManagerFactory.setInstance(new CTCSurfaceManagerFactory()); } + public static CTCGraphicsEnvironment getInstance() { + return INSTANCE; + } @Override protected int getNumScreens() { return 1; diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java index fd828e3..ed10e94 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java @@ -12,7 +12,7 @@ public class CTCInterceptor { - private static CTCGraphicsEnvironment ctcGraphicsEnvironment = new CTCGraphicsEnvironment(); + private static CTCGraphicsEnvironment ctcGraphicsEnvironment = CTCGraphicsEnvironment.getInstance(); @RuntimeType public static GraphicsEnvironment intercept1(@This Object self, diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java index 09380ab..7234549 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java @@ -150,7 +150,7 @@ public static void injectCTCGraphicsEnvironment() throws ClassNotFoundException, } public static class GraphicsEnvironmentInterceptor { - private static CTCGraphicsEnvironment ctcGraphicsEnvironment = new CTCGraphicsEnvironment(); + private static CTCGraphicsEnvironment ctcGraphicsEnvironment = CTCGraphicsEnvironment.getInstance(); @RuntimeType public static Object intercept(@Origin Method method, @AllArguments final Object[] args) throws Exception { return ctcGraphicsEnvironment; From f50727aeacbc85f972601e1ec676b386a55bd839 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Fri, 9 Feb 2024 11:37:54 +0100 Subject: [PATCH 10/15] Keep constructor public, no need to change the API --- .../caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java index 07c7abe..5cdfbf7 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/CTCGraphicsEnvironment.java @@ -32,7 +32,7 @@ public class CTCGraphicsEnvironment extends SunGraphicsEnvironment { private static final CTCGraphicsEnvironment INSTANCE = new CTCGraphicsEnvironment(); - private CTCGraphicsEnvironment() { + public CTCGraphicsEnvironment() { SurfaceManagerFactory.setInstance(new CTCSurfaceManagerFactory()); } From f9335233a20fb874f4ead4baefbe787b9d1a30a3 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Fri, 9 Feb 2024 11:38:23 +0100 Subject: [PATCH 11/15] Remove redundant code --- .../cacio/ctc/junit/CTCInterceptor.java | 14 ++------------ .../cacio/ctc/junit/CacioExtension.java | 3 +-- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java index ed10e94..e1b6c18 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java @@ -12,18 +12,8 @@ public class CTCInterceptor { - private static CTCGraphicsEnvironment ctcGraphicsEnvironment = CTCGraphicsEnvironment.getInstance(); - - @RuntimeType - public static GraphicsEnvironment intercept1(@This Object self, - @Origin Method method, - @AllArguments Object[] args, - @SuperMethod Method superMethod) { - return ctcGraphicsEnvironment; - } - @RuntimeType - public static GraphicsEnvironment intercept2() { - return ctcGraphicsEnvironment; + public static GraphicsEnvironment intercept() { + return CTCGraphicsEnvironment.getInstance(); } } diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java index 7234549..1fc486c 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java @@ -150,10 +150,9 @@ public static void injectCTCGraphicsEnvironment() throws ClassNotFoundException, } public static class GraphicsEnvironmentInterceptor { - private static CTCGraphicsEnvironment ctcGraphicsEnvironment = CTCGraphicsEnvironment.getInstance(); @RuntimeType public static Object intercept(@Origin Method method, @AllArguments final Object[] args) throws Exception { - return ctcGraphicsEnvironment; + return CTCGraphicsEnvironment.getInstance(); } } From 2e3d176e7b498d123cf7c0dc886bd107c4af9d33 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Fri, 9 Feb 2024 11:39:06 +0100 Subject: [PATCH 12/15] Make changing the Java version easier --- cacio-shared/pom.xml | 4 ++-- cacio-tta/pom.xml | 4 ++-- pom.xml | 8 ++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cacio-shared/pom.xml b/cacio-shared/pom.xml index 1db89e5..bd69d1f 100644 --- a/cacio-shared/pom.xml +++ b/cacio-shared/pom.xml @@ -21,8 +21,8 @@ maven-compiler-plugin 3.8.1 - 17 - 17 + ${cacio.java.version} + ${cacio.java.version} -XDignore.symbol.file=true --add-exports=java.desktop/java.awt.peer=ALL-UNNAMED diff --git a/cacio-tta/pom.xml b/cacio-tta/pom.xml index bbadf41..3ecf9d7 100644 --- a/cacio-tta/pom.xml +++ b/cacio-tta/pom.xml @@ -65,8 +65,8 @@ maven-compiler-plugin 3.8.1 - 17 - 17 + ${cacio.java.version} + ${cacio.java.version} -XDignore.symbol.file=true --add-exports=java.desktop/java.awt.peer=ALL-UNNAMED diff --git a/pom.xml b/pom.xml index 62329ae..5c65a0a 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,10 @@ cacio-tta + + 17 + + ossrh @@ -112,8 +116,8 @@ maven-compiler-plugin 3.8.1 - 17 - 17 + ${cacio.java.version} + ${cacio.java.version} -XDignore.symbol.file=true From 3bce65bb76ccf22724a4a9e7c6c82652f13f68c8 Mon Sep 17 00:00:00 2001 From: janblom Date: Fri, 9 Feb 2024 15:19:57 +0100 Subject: [PATCH 13/15] Update CacioExtension.java restore manipulation of sun.java2d.SurfaceManagerFactory --- .../caciocavallosilano/cacio/ctc/junit/CacioExtension.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java index 5626bde..7ddc219 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java @@ -89,7 +89,10 @@ public class CacioExtension implements ExecutionCondition { defaultHeadlessField.set(null, Boolean.FALSE); headlessField.set(null, Boolean.FALSE); - + Class smfCls = Class.forName("sun.java2d.SurfaceManagerFactory"); + Field smf = smfCls.getDeclaredField("instance"); + smf.setAccessible(true); + smf.set(null, null); } catch (Exception e) { e.printStackTrace(); } From 1080aa421439c2c2f12bf3b80be323b847597a05 Mon Sep 17 00:00:00 2001 From: Jan Blom Date: Sat, 10 Feb 2024 08:03:20 +0100 Subject: [PATCH 14/15] Some cleanups --- .../caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java | 5 ----- .../caciocavallosilano/cacio/ctc/junit/CacioExtension.java | 5 +---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java index e1b6c18..c2464a9 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CTCInterceptor.java @@ -1,14 +1,9 @@ package com.github.caciocavallosilano.cacio.ctc.junit; import com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment; -import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bind.annotation.*; -import org.junit.jupiter.api.extension.ConditionEvaluationResult; -import org.junit.jupiter.api.extension.ExecutionCondition; -import org.junit.jupiter.api.extension.ExtensionContext; import java.awt.*; -import java.lang.reflect.Method; public class CTCInterceptor { diff --git a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java index 7ddc219..ad5c44c 100644 --- a/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java +++ b/cacio-tta/src/main/java/com/github/caciocavallosilano/cacio/ctc/junit/CacioExtension.java @@ -81,12 +81,9 @@ public class CacioExtension implements ExecutionCondition { Field defaultHeadlessField = java.awt.GraphicsEnvironment.class.getDeclaredField("defaultHeadless"); defaultHeadlessField.setAccessible(true); - defaultHeadlessField.set(null, Boolean.TRUE); + defaultHeadlessField.set(null, Boolean.FALSE); Field headlessField = java.awt.GraphicsEnvironment.class.getDeclaredField("headless"); headlessField.setAccessible(true); - headlessField.set(null, Boolean.TRUE); - - defaultHeadlessField.set(null, Boolean.FALSE); headlessField.set(null, Boolean.FALSE); Class smfCls = Class.forName("sun.java2d.SurfaceManagerFactory"); From 150e3b0565eab08261f4f4c584b2607b4ced73e1 Mon Sep 17 00:00:00 2001 From: janblom Date: Tue, 13 Feb 2024 17:06:06 +0100 Subject: [PATCH 15/15] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ffbe64e..d9347ea 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,6 @@ [![License](https://img.shields.io/github/license/CaciocavalloSilano/caciocavallo.svg)](https://raw.githubusercontent.com/CaciocavalloSilano/caciocavallo/master/LICENSE) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.caciocavallosilano/cacio-tta/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.caciocavallosilano/cacio-tta) -## Please note -This is a fork of the Caciocavallo project. It's purpose is to enable using a fixed version in the OHDSI Rabbit tools (for Java 18+) until this fix is available in the -parent project. An issue for this has been created in the parent project, with an offer to create a PR. (Jan Blom) - ## Introduction One problem with running GUI tests is that they need to create windows, grab keyboard focus, and do all sorts of interaction with the screen. @@ -136,6 +132,8 @@ This is because Java only allows to set the toolkit once, and it cannot be unloa The `add-exports` and `add-opens` jvm args are required with Java 17, since these are internal packages that aren't exported, these can't be added to a `module-info.java` file. +With Java 18+, you may also want to add a argument to suppress warnings about an agent (ByteBuddyAgent) being loaded: `-XX:+EnableDynamicAgentLoading` . + You can change the resolution of the virtual screen by setting the `cacio.managed.screensize` system property. For example: