diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f80e1f7..88fdb68c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ### 0.4.4-SNAPSHOT (TBD) +#### Features +* Merge the content of `internal-openlink`, `internal-util`, `freeze-detector` and `tree-showing` modules under the +`jpro-utils` module. This module will offer essential tools to enhance the development of **JPro/JavaFX** applications. + ---------------------- ### 0.4.3 (October 31, 2024) diff --git a/README.md b/README.md index 9fd2dfd5..f2c61aa9 100644 --- a/README.md +++ b/README.md @@ -327,6 +327,27 @@ dependencies { } ``` +## JPro Utils +This library offers essential tools for various functionalities to enhance the development of **JPro/JavaFX** applications. + +#### Maven configuration +```xml + + + one.jpro.platform + jpro-utils + 0.4.4-SNAPSHOT + + +``` + +#### Gradle configuration +```groovy +dependencies { + implementation 'one.jpro.platform:jpro-utils:0.4.4-SNAPSHOT' +} +``` + ## JPro WebRTC This library provides an API to use WebRTC in **JPro/JavaFX** applications. This technology allows for the direct exchange of audio, video, and data between web browsers or devices, facilitating features like video conferencing, @@ -416,72 +437,6 @@ dependencies { } ``` -## TreeShowing -#### Motivation -In JavaFX, when a node could be collected - it's often prevented by ongoing animation or background tasks. -For this reason, it's often necessary to stop the animation or background task, -when a node is no longer used. -Internally in JavaFX, the property "treeShowing" is used, to check whether a Node is still used. -But as an end-user, this property is not accessible in their application. -With this project, we want to make the property available to the common JavaFX Developer - allowing them to write -bug-free and leak-free applications. - -#### Maven configuration -```xml - - one.jpro.platform - tree-showing - 0.4.4-SNAPSHOT - -``` - -#### Gradle configuration -```groovy -dependencies { - implementation 'one.jpro.platform:tree-showing:0.4.4-SNAPSHOT' -} -``` - -#### Typical Usage: -``` -Timeline myTimeline = new Timeline(); -myTimeline.setCycleCount(Timeline.INDEFINITE); -Node node = -treeShowing = TreeShowing.treeShowing(node); -treeShowing.addListener((p,o,showing) -> { - if(showing) { - myTimeline.start(); - } else { - myTimeline.stop(); - } -}); -if(treeShowing.get()) { - myTimeline.start(); -} -``` - -## Freeze Detector -This library allows you to track whether the JavaFX Application Thread is frozen for a given time. -This can be useful for debugging purposes, detecting deadlocks or other optimize performance. - -#### Maven configuration -```xml - - - one.jpro.platform - freeze-detector - 0.4.4-SNAPSHOT - - -``` - -#### Gradle configuration -```groovy -dependencies { - implementation 'one.jpro.platform:freeze-detector:0.4.4-SNAPSHOT' -} -``` - ## Launch the examples To run the examples, you can use the following commands: diff --git a/build.gradle b/build.gradle index 53ccc40c..ddf9005d 100644 --- a/build.gradle +++ b/build.gradle @@ -47,6 +47,9 @@ configure(subprojects.findAll { it.name != 'example' }) { testImplementation "org.junit.jupiter:junit-jupiter-engine:$JUNIT_VERSION" testImplementation "one.jpro:jmemorybuddy:$JMEMORYBUDDY_VERSION" + testImplementation "one.jpro.platform.jpms:testfx-junit5:$TESTFX_VERSION" + testImplementation "one.jpro.platform.jpms:testfx-core:$TESTFX_VERSION" + testImplementation "one.jpro.platform.jpms:openjfx-monocle:$MONOCLE_VERSION" testImplementation "org.mockito:mockito-core:$MOCKITO_VERSION" testImplementation "org.mockito:mockito-junit-jupiter:$MOCKITO_VERSION" testImplementation "org.hamcrest:hamcrest:$HAMCREST_VERSION" @@ -79,12 +82,11 @@ configure(subprojects.findAll { it.name != 'example' }) { } } -configure([project("tree-showing"), project("jpro-auth:core"), project("jpro-auth:routing"), project("jpro-file"), +configure([project("jpro-auth:core"), project("jpro-auth:routing"), project("jpro-file"), project("jpro-image-manager"), project("jpro-mail"), project("jpro-mdfx"), project("jpro-media"), - project("jpro-scenegraph"), project("jpro-session"), project("jpro-sipjs"), - project("jpro-html-scrollpane"), project("freeze-detector"), project("jpro-routing:core"), - project("jpro-routing:dev"), project("jpro-routing:popup"), project("jpro-webrtc"), project("jpro-youtube"), - project("internal:openlink"), project("internal:util")]) { + project("jpro-scenegraph"), project("jpro-session"), project("jpro-sipjs"), project("jpro-utils"), + project("jpro-html-scrollpane"), project("jpro-routing:core"), project("jpro-routing:dev"), + project("jpro-routing:popup"), project("jpro-webrtc"), project("jpro-youtube")]) { apply plugin: 'maven-publish' apply plugin: 'signing' apply plugin: 'tech.yanand.maven-central-publish' @@ -96,13 +98,7 @@ configure([project("tree-showing"), project("jpro-auth:core"), project("jpro-aut publications { mavenJava(MavenPublication) { groupId = 'one.jpro.platform' - - if (project.path.startsWith(":internal")) { - artifactId = "jpro-${isParentASubproject ? "${project.parent.name}-${project.name}" : "${project.name}"}" - } else { - artifactId = isParentASubproject ? "${project.parent.name}-${project.name}" : "${project.name}" - } - + artifactId = isParentASubproject ? "${project.parent.name}-${project.name}" : "${project.name}" version = JPRO_PLATFORM_VERSION // Ensure only one component is included in the publication diff --git a/freeze-detector/src/main/java/module-info.java b/freeze-detector/src/main/java/module-info.java deleted file mode 100644 index ccf5e023..00000000 --- a/freeze-detector/src/main/java/module-info.java +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Freeze detector module descriptor. - * - * @author Besmir Beqiri - */ -module one.jpro.platform.freezedetector { - requires javafx.graphics; - - exports one.jpro.platform.freezedetector; -} \ No newline at end of file diff --git a/freeze-detector/src/test/java/module-info.java b/freeze-detector/src/test/java/module-info.java deleted file mode 100644 index 83696850..00000000 --- a/freeze-detector/src/test/java/module-info.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Module descriptor for the JPro Platform Freeze Detector Test module. - * - * @author Besmir Beqiri - */ -module one.jpro.platform.freezedetector.test { - requires one.jpro.platform.freezedetector; - requires org.slf4j; - - requires org.junit.jupiter; - requires org.testfx.core; - requires org.testfx.junit5; - requires org.assertj.core; - - opens one.jpro.platform.freezedetector.test; -} \ No newline at end of file diff --git a/internal/openlink/build.gradle b/internal/openlink/build.gradle deleted file mode 100644 index 17be7fcb..00000000 --- a/internal/openlink/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - -dependencies { - implementation project(":internal:util") - implementation "org.jetbrains:annotations:$JETBRAINS_ANNOTATIONS_VERSION" - implementation "org.slf4j:slf4j-api:$SLF4J_API_VERSION" - - testImplementation("org.mockito:mockito-core:$MOCKITO_VERSION") - testImplementation("org.mockito:mockito-junit-jupiter:$MOCKITO_VERSION") - testRuntimeOnly "ch.qos.logback:logback-classic:$LOGBACK_VERSION" -} - -publishing { - publications { - mavenJava(MavenPublication) { - pom { - name = 'JPro Open Link' - description = 'A module that helps launching the default browser of the running platform.' - } - } - } -} \ No newline at end of file diff --git a/internal/openlink/src/main/java/module-info.java b/internal/openlink/src/main/java/module-info.java deleted file mode 100644 index 256ec5bd..00000000 --- a/internal/openlink/src/main/java/module-info.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * The module descriptor for the OpenLink module. - * - * @author Besmir Beqiri - */ -module one.jpro.platform.internal.openlink { - requires javafx.controls; - requires org.jetbrains.annotations; - requires org.slf4j; - - requires one.jpro.platform.internal.util; - - exports one.jpro.platform.internal.openlink; -} \ No newline at end of file diff --git a/internal/openlink/src/test/java/one/jpro/platform/internal/openlink/test/OpenLinkTests.java b/internal/openlink/src/test/java/one/jpro/platform/internal/openlink/test/OpenLinkTests.java deleted file mode 100644 index 1ff48b29..00000000 --- a/internal/openlink/src/test/java/one/jpro/platform/internal/openlink/test/OpenLinkTests.java +++ /dev/null @@ -1,68 +0,0 @@ -package one.jpro.platform.internal.openlink.test; - -import one.jpro.platform.internal.openlink.OpenLink; -import one.jpro.platform.internal.util.PlatformUtils; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; - -import java.io.IOException; -import java.net.URI; - -import static org.mockito.Mockito.*; - -/** - * OpenLink tests. - * - * @author Besmir Beqiri - */ -public class OpenLinkTests { - - @Mock - private static Runtime runtime; - - @BeforeAll - public static void setUp() { - mockStatic(Runtime.class); - mockStatic(PlatformUtils.class); - - runtime = mock(Runtime.class); - when(Runtime.getRuntime()).thenReturn(runtime); - } - - @Test - public void testOpenURLOnMac() throws IOException { - when(PlatformUtils.isDesktop()).thenReturn(true); - when(PlatformUtils.isMac()).thenReturn(true); - when(PlatformUtils.isWindows()).thenReturn(false); - when(PlatformUtils.isLinux()).thenReturn(false); - - OpenLink.openURL(URI.create("http://www.example.com").toURL()); - - verify(runtime).exec(new String[] {"open", "http://www.example.com"}); - } - - @Test - public void testOpenURLOnWindows() throws IOException { - when(PlatformUtils.isDesktop()).thenReturn(true); - when(PlatformUtils.isMac()).thenReturn(false); - when(PlatformUtils.isWindows()).thenReturn(true); - when(PlatformUtils.isLinux()).thenReturn(false); - - OpenLink.openURL(URI.create("http://www.example.com").toURL()); - - verify(runtime).exec(new String[] {"rundll32","url.dll,FileProtocolHandler", "http://www.example.com"}); - } - - @Test - public void testOpenURLOnLinux() throws IOException { - when(PlatformUtils.isDesktop()).thenReturn(true); - when(PlatformUtils.isMac()).thenReturn(false); - when(PlatformUtils.isWindows()).thenReturn(false); - when(PlatformUtils.isLinux()).thenReturn(true); - - OpenLink.openURL(URI.create("http://www.example.com").toURL()); - - verify(runtime).exec(new String[] {"xdg-open", "http://www.example.com"}); - } -} diff --git a/internal/util/build.gradle b/internal/util/build.gradle deleted file mode 100644 index a9154196..00000000 --- a/internal/util/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - -dependencies { - implementation "org.jetbrains:annotations:$JETBRAINS_ANNOTATIONS_VERSION" - implementation "org.slf4j:slf4j-api:$SLF4J_API_VERSION" - - testImplementation "org.assertj:assertj-core:$ASSERTJ_VERSION" - testRuntimeOnly "ch.qos.logback:logback-classic:$LOGBACK_VERSION" -} - -publishing { - publications { - mavenJava(MavenPublication) { - pom { - name = 'JPro Utils' - description = 'A utility module offering essential tools for process management and platform-specific functionalities' - } - } - } -} diff --git a/internal/util/src/main/java/module-info.java b/internal/util/src/main/java/module-info.java deleted file mode 100644 index 6901ba00..00000000 --- a/internal/util/src/main/java/module-info.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * The module descriptor for the internal Process module. - * - * @author Besmir Beqiri - */ -module one.jpro.platform.internal.util { - requires org.jetbrains.annotations; - requires org.slf4j; - - exports one.jpro.platform.internal.util; -} \ No newline at end of file diff --git a/jpro-auth/core/build.gradle b/jpro-auth/core/build.gradle index 1e35b6c5..53f54bb6 100755 --- a/jpro-auth/core/build.gradle +++ b/jpro-auth/core/build.gradle @@ -3,7 +3,7 @@ plugins { } dependencies { - implementation project(":internal:openlink") + api project(":jpro-utils") implementation "com.sandec.jpro:jpro-webapi:$JPRO_VERSION" implementation "com.auth0:java-jwt:$AUTH0_JAVAJWT_VERSION" implementation "one.jpro.platform.jpms:jwks-rsa:$AUTH0_JWKSRSA_VERSION" diff --git a/jpro-auth/core/src/main/java/module-info.java b/jpro-auth/core/src/main/java/module-info.java index 5a3a6498..39ad825b 100644 --- a/jpro-auth/core/src/main/java/module-info.java +++ b/jpro-auth/core/src/main/java/module-info.java @@ -8,13 +8,13 @@ requires transitive javafx.controls; requires transitive org.json; requires transitive org.slf4j; + requires transitive one.jpro.platform.utils; requires org.jetbrains.annotations; requires java.net.http; requires jpro.webapi; requires jwks.rsa; requires com.auth0.jwt; - requires one.jpro.platform.internal.openlink; opens one.jpro.platform.auth.core; opens one.jpro.platform.auth.core.authentication; diff --git a/jpro-auth/core/src/main/java/one/jpro/platform/auth/core/http/impl/HttpServerImpl.java b/jpro-auth/core/src/main/java/one/jpro/platform/auth/core/http/impl/HttpServerImpl.java index b90485f7..aae04178 100644 --- a/jpro-auth/core/src/main/java/one/jpro/platform/auth/core/http/impl/HttpServerImpl.java +++ b/jpro-auth/core/src/main/java/one/jpro/platform/auth/core/http/impl/HttpServerImpl.java @@ -6,7 +6,7 @@ import one.jpro.platform.auth.core.http.HttpServer; import one.jpro.platform.auth.core.http.HttpServerException; import one.jpro.platform.auth.core.http.HttpStatus; -import one.jpro.platform.internal.openlink.OpenLink; +import one.jpro.platform.utils.OpenLink; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; diff --git a/jpro-html-scrollpane/build.gradle b/jpro-html-scrollpane/build.gradle index 1613301e..b1959d30 100644 --- a/jpro-html-scrollpane/build.gradle +++ b/jpro-html-scrollpane/build.gradle @@ -1,5 +1,5 @@ dependencies { - implementation project(":tree-showing") + api project(":jpro-utils") implementation "com.sandec.jpro:jpro-webapi:$JPRO_VERSION" implementation "one.jpro:jmemorybuddy:$JMEMORYBUDDY_VERSION" implementation "org.slf4j:slf4j-api:$SLF4J_API_VERSION" @@ -9,10 +9,8 @@ publishing { publications { mavenJava(MavenPublication) { pom { - name = 'JPro Auth Core' - description = 'A module for adding advanced authentication and authorization to JPro/JavaFX ' + - 'applications, supporting OAuth 2.0, OpenID Connect, integration with multiple identity ' + - 'providers and asynchronous operations for enhanced performance.' + name = 'JPro HTML Scrollpane Skin' + description = 'A module containing a ScrollPane skin for HTML content' } } } diff --git a/jpro-html-scrollpane/src/main/java/module-info.java b/jpro-html-scrollpane/src/main/java/module-info.java index ad4b994b..c6064ddc 100644 --- a/jpro-html-scrollpane/src/main/java/module-info.java +++ b/jpro-html-scrollpane/src/main/java/module-info.java @@ -1,8 +1,7 @@ module one.jpro.platform.htmlscrollpane { - requires javafx.controls; + requires transitive one.jpro.platform.utils; requires jpro.webapi; requires one.jpro.jmemorybuddy; - requires one.jpro.platform.treeshowing; requires org.slf4j; exports one.jpro.platform.htmlscrollpane; diff --git a/jpro-html-scrollpane/src/main/java/one/jpro/platform/htmlscrollpane/HTMLScrollPaneSkin.java b/jpro-html-scrollpane/src/main/java/one/jpro/platform/htmlscrollpane/HTMLScrollPaneSkin.java index b37330b4..5bb95a69 100644 --- a/jpro-html-scrollpane/src/main/java/one/jpro/platform/htmlscrollpane/HTMLScrollPaneSkin.java +++ b/jpro-html-scrollpane/src/main/java/one/jpro/platform/htmlscrollpane/HTMLScrollPaneSkin.java @@ -16,7 +16,7 @@ import javafx.scene.layout.StackPane; import javafx.stage.PopupWindow; import javafx.stage.Window; -import one.jpro.platform.treeshowing.TreeShowing; +import one.jpro.platform.utils.TreeShowing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +25,74 @@ import java.util.List; import java.util.Random; +/** + * The {@code HTMLScrollPaneSkin} class provides a custom skin for the JavaFX {@link ScrollPane} + * control, enabling the integration of HTML content within the scroll pane. This skin leverages + * the {@link HTMLView} component to render HTML content and manages resource cleanup to prevent + * memory leaks by monitoring the visibility of the associated {@code ScrollPane} using the + * {@link TreeShowing} utility. + * + *

+ * In JavaFX applications, embedding HTML content within UI components like {@code ScrollPane} + * can lead to resource retention issues if animations or background tasks continue running + * after the node is no longer visible. The {@code HTMLScrollPaneSkin} addresses this by: + *

+ * + * + * + *

+ * By using {@code HTMLScrollPaneSkin}, developers can seamlessly integrate HTML content into + * scrollable regions of their JavaFX applications while maintaining optimal memory and resource + * management. + *

+ * + *

+ * Example Usage: + *

+ * + *
{@code
+ * import javafx.application.Application;
+ * import javafx.scene.Scene;
+ * import javafx.scene.control.ScrollPane;
+ * import javafx.stage.Stage;
+ *
+ * public class HTMLScrollPaneExample extends Application {
+ *     @Override
+ *     public void start(Stage primaryStage) {
+ *         ScrollPane scrollPane = new ScrollPane();
+ *
+ *         // Apply the custom HTMLScrollPaneSkin
+ *         scrollPane.setSkin(new HTMLScrollPaneSkin(scrollPane, "additional-attributes"));
+ *
+ *         Scene scene = new Scene(scrollPane, 800, 600);
+ *         primaryStage.setScene(scene);
+ *         primaryStage.setTitle("HTMLScrollPaneSkin Example");
+ *         primaryStage.show();
+ *     }
+ *
+ *     public static void main(String[] args) {
+ *         launch(args);
+ *     }
+ * }
+ * }
+ * + *

+ * In this example, a {@code ScrollPane} is created and assigned the {@code HTMLScrollPaneSkin} + * with additional attributes. The skin manages the embedding of HTML content and ensures that + * resources are properly cleaned up when the scroll pane is no longer part of the scene graph. + *

+ * + * @see ScrollPane + * @see HTMLView + * @see TreeShowing + * + * @author Florian Kirmaier + */ public class HTMLScrollPaneSkin extends SkinBase { private static final Logger logger = LoggerFactory.getLogger(HTMLScrollPaneSkin.class); diff --git a/jpro-routing/build.gradle b/jpro-routing/build.gradle index 12e59ef4..cce56d9d 100644 --- a/jpro-routing/build.gradle +++ b/jpro-routing/build.gradle @@ -34,8 +34,8 @@ configure([project(':jpro-routing:core'), project(':jpro-routing:dev'), project( configure([project(':jpro-routing:core')]) { dependencies { + api project(":jpro-utils") api "SANDEC:simplefx_2.12:$SIMPLEFX_VERSION" - api project(":internal:openlink") } publishing { diff --git a/jpro-routing/core/src/main/java/module-info.java b/jpro-routing/core/src/main/java/module-info.java index 39b293c0..07a1786b 100644 --- a/jpro-routing/core/src/main/java/module-info.java +++ b/jpro-routing/core/src/main/java/module-info.java @@ -4,13 +4,13 @@ requires transitive de.sandec.jnodes; requires transitive one.jpro.jmemorybuddy; + requires transitive one.jpro.platform.utils; requires transitive jpro.webapi; requires transitive simplefx.core; requires transitive simplefx.utility; requires transitive simplefx.wrapping; requires transitive simplefx.extended; requires transitive scala.library; - requires transitive one.jpro.platform.internal.openlink; exports one.jpro.platform.routing; exports one.jpro.platform.routing.crawl; diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/LinkUtil.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/LinkUtil.scala index 2d4b65a9..1dc7c7a8 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/LinkUtil.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/LinkUtil.scala @@ -12,7 +12,7 @@ import org.slf4j.{Logger, LoggerFactory} object LinkUtil { private var openLinkExternalFun: String => Unit = { link => - import one.jpro.platform.internal.openlink.OpenLink + import one.jpro.platform.utils.OpenLink OpenLink.openURL(link) } def setOpenLinkExternalFun(x: String => Unit): Unit = { diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManager.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManager.scala index 324d0958..b3a9803e 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManager.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManager.scala @@ -9,7 +9,7 @@ import org.slf4j.{Logger, LoggerFactory} import simplefx.all._ import simplefx.core._ import simplefx.experimental._ -import one.jpro.platform.internal.openlink.OpenLink +import one.jpro.platform.utils.OpenLink import java.net.URI import java.util.function.Consumer diff --git a/freeze-detector/build.gradle b/jpro-utils/build.gradle similarity index 57% rename from freeze-detector/build.gradle rename to jpro-utils/build.gradle index 6145b4e0..12c1c96d 100644 --- a/freeze-detector/build.gradle +++ b/jpro-utils/build.gradle @@ -1,9 +1,13 @@ +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + dependencies { - api "org.slf4j:slf4j-api:$SLF4J_API_VERSION" + implementation "org.jetbrains:annotations:$JETBRAINS_ANNOTATIONS_VERSION" + implementation "org.slf4j:slf4j-api:$SLF4J_API_VERSION" - testImplementation "one.jpro.platform.jpms:testfx-junit5:$TESTFX_VERSION" - testImplementation "one.jpro.platform.jpms:testfx-core:$TESTFX_VERSION" - testImplementation "one.jpro.platform.jpms:openjfx-monocle:$MONOCLE_VERSION" + testImplementation "one.jpro:jmemorybuddy:$JMEMORYBUDDY_VERSION" } test { @@ -22,9 +26,9 @@ publishing { publications { mavenJava(MavenPublication) { pom { - name = 'Freeze Detector' - description = 'A module that allows you to track whether the JavaFX Application Thread is frozen for ' + - 'a given time, useful for debugging purposes, detecting deadlocks, or optimizing performance.' + name = 'JPro Utils' + description = 'A utility module offering essential tools for various functionalities to enhance the ' + + 'development of **JPro/JavaFX** applications' } } } diff --git a/jpro-utils/src/main/java/module-info.java b/jpro-utils/src/main/java/module-info.java new file mode 100644 index 00000000..6a0e55ab --- /dev/null +++ b/jpro-utils/src/main/java/module-info.java @@ -0,0 +1,12 @@ +/** + * The module descriptor for the JPro Utils module. + * + * @author Besmir Beqiri + */ +module one.jpro.platform.utils { + requires transitive javafx.controls; + requires org.jetbrains.annotations; + requires org.slf4j; + + exports one.jpro.platform.utils; +} \ No newline at end of file diff --git a/internal/util/src/main/java/one/jpro/platform/internal/util/CommandRunner.java b/jpro-utils/src/main/java/one/jpro/platform/utils/CommandRunner.java similarity index 99% rename from internal/util/src/main/java/one/jpro/platform/internal/util/CommandRunner.java rename to jpro-utils/src/main/java/one/jpro/platform/utils/CommandRunner.java index d8a94155..598c762e 100644 --- a/internal/util/src/main/java/one/jpro/platform/internal/util/CommandRunner.java +++ b/jpro-utils/src/main/java/one/jpro/platform/utils/CommandRunner.java @@ -1,4 +1,4 @@ -package one.jpro.platform.internal.util; +package one.jpro.platform.utils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/freeze-detector/src/main/java/one/jpro/platform/freezedetector/FreezeDetector.java b/jpro-utils/src/main/java/one/jpro/platform/utils/FreezeDetector.java similarity index 89% rename from freeze-detector/src/main/java/one/jpro/platform/freezedetector/FreezeDetector.java rename to jpro-utils/src/main/java/one/jpro/platform/utils/FreezeDetector.java index 9610e125..d5a15b04 100644 --- a/freeze-detector/src/main/java/one/jpro/platform/freezedetector/FreezeDetector.java +++ b/jpro-utils/src/main/java/one/jpro/platform/utils/FreezeDetector.java @@ -1,4 +1,4 @@ -package one.jpro.platform.freezedetector; +package one.jpro.platform.utils; import javafx.animation.AnimationTimer; import javafx.application.Platform; @@ -6,6 +6,12 @@ import java.time.Duration; import java.util.function.BiConsumer; +/** + * This class allows you to track whether the JavaFX Application Thread is frozen for a given time. + * This can be useful for debugging purposes, detecting deadlocks or other optimize performance. + * + * @author Florian Kirmaier + */ public class FreezeDetector { Thread fxthread; diff --git a/internal/openlink/src/main/java/one/jpro/platform/internal/openlink/OpenLink.java b/jpro-utils/src/main/java/one/jpro/platform/utils/OpenLink.java similarity index 94% rename from internal/openlink/src/main/java/one/jpro/platform/internal/openlink/OpenLink.java rename to jpro-utils/src/main/java/one/jpro/platform/utils/OpenLink.java index 07cce160..1a8cba1c 100644 --- a/internal/openlink/src/main/java/one/jpro/platform/internal/openlink/OpenLink.java +++ b/jpro-utils/src/main/java/one/jpro/platform/utils/OpenLink.java @@ -1,6 +1,5 @@ -package one.jpro.platform.internal.openlink; +package one.jpro.platform.utils; -import one.jpro.platform.internal.util.PlatformUtils; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; diff --git a/internal/util/src/main/java/one/jpro/platform/internal/util/PlatformUtils.java b/jpro-utils/src/main/java/one/jpro/platform/utils/PlatformUtils.java similarity index 98% rename from internal/util/src/main/java/one/jpro/platform/internal/util/PlatformUtils.java rename to jpro-utils/src/main/java/one/jpro/platform/utils/PlatformUtils.java index e62989cf..ef299fed 100644 --- a/internal/util/src/main/java/one/jpro/platform/internal/util/PlatformUtils.java +++ b/jpro-utils/src/main/java/one/jpro/platform/utils/PlatformUtils.java @@ -1,4 +1,4 @@ -package one.jpro.platform.internal.util; +package one.jpro.platform.utils; /** * Platform utilities. diff --git a/tree-showing/src/main/java/one/jpro/platform/treeshowing/TreeShowing.java b/jpro-utils/src/main/java/one/jpro/platform/utils/TreeShowing.java similarity index 52% rename from tree-showing/src/main/java/one/jpro/platform/treeshowing/TreeShowing.java rename to jpro-utils/src/main/java/one/jpro/platform/utils/TreeShowing.java index e5543b89..a1b155d5 100644 --- a/tree-showing/src/main/java/one/jpro/platform/treeshowing/TreeShowing.java +++ b/jpro-utils/src/main/java/one/jpro/platform/utils/TreeShowing.java @@ -1,4 +1,4 @@ -package one.jpro.platform.treeshowing; +package one.jpro.platform.utils; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -7,6 +7,78 @@ import javafx.scene.Scene; import javafx.stage.Window; +/** + * The {@code TreeShowing} class provides access to the internal {@code "treeShowing"} property of + * JavaFX {@link javafx.scene.Node Nodes}. This property is essential for determining whether a + * {@code Node} is currently part of the scene graph and actively being displayed. + * + *

+ * In JavaFX, a {@code Node} eligible for garbage collection might still be retained in memory + * due to ongoing animations or background tasks. The internal {@code "treeShowing"} property + * tracks the visibility and usage of each {@code Node}, but it is not exposed through the standard + * JavaFX API. The {@code TreeShowing} class bridges this gap, enabling developers to monitor + * a node's lifecycle and manage resources effectively to prevent memory leaks. + *

+ * + *

+ * By utilizing the {@code TreeShowing} class, developers can: + *

+ * + * + * + *

+ * This facilitates the creation of robust, memory-efficient JavaFX applications by ensuring that + * unused nodes do not retain unnecessary resources. + *

+ * + *

+ * Example Usage: + *

+ * + *
{@code
+ * import javafx.animation.Timeline;
+ * import javafx.scene.Node;
+ *
+ * public class Example {
+ *     public void setupNode(Node myNode) {
+ *         // Create a Timeline animation
+ *         Timeline myTimeline = new Timeline();
+ *         myTimeline.setCycleCount(Timeline.INDEFINITE);
+ *
+ *         // Obtain the TreeShowing instance for the node
+ *         TreeShowing treeShowing = TreeShowing.treeShowing(myNode);
+ *
+ *         // Add a listener to respond to changes in the treeShowing property
+ *         treeShowing.addListener((observable, oldValue, showing) -> {
+ *             if (showing) {
+ *                 // Start the animation when the node is part of the scene graph
+ *                 myTimeline.play();
+ *             } else {
+ *                 // Stop the animation when the node is removed from the scene graph
+ *                 myTimeline.stop();
+ *             }
+ *         });
+ *
+ *         // Immediately start the animation if the node is already showing
+ *         if (treeShowing.get()) {
+ *             myTimeline.play();
+ *         }
+ *     }
+ * }
+ * }
+ * + *

+ * In this example, the listener monitors the {@code "treeShowing"} property of {@code myNode}. + * When {@code myNode} is added to the scene graph, the animation starts. Conversely, when + * {@code myNode} is removed, the animation stops, ensuring that resources are managed + * efficiently. + *

+ * + * @author Florian Kirmaier + */ public class TreeShowing { private static final Object KEY_TREE_SHOWING = new Object(); diff --git a/jpro-utils/src/test/java/module-info.java b/jpro-utils/src/test/java/module-info.java new file mode 100644 index 00000000..3e8a6ef6 --- /dev/null +++ b/jpro-utils/src/test/java/module-info.java @@ -0,0 +1,21 @@ +/** + * Module descriptor for the JPro Utils Test module. + * + * @author Besmir Beqiri + */ +module one.jpro.platform.utils.test { + requires one.jpro.platform.utils; + requires one.jpro.jmemorybuddy; + requires org.slf4j; + + requires org.junit.jupiter; + requires org.testfx.core; + requires org.testfx.junit5; + requires org.assertj.core; + requires org.mockito; + requires org.mockito.junit.jupiter; + requires jdk.attach; // Required by Mockito, for Java 21 and later seems to not be required anymore since transitive + + exports one.jpro.platform.utils.test; + opens one.jpro.platform.utils.test; +} \ No newline at end of file diff --git a/internal/util/src/test/java/one/jpro/platform/internal/util/CommandRunnerTests.java b/jpro-utils/src/test/java/one/jpro/platform/utils/test/CommandRunnerTests.java similarity index 97% rename from internal/util/src/test/java/one/jpro/platform/internal/util/CommandRunnerTests.java rename to jpro-utils/src/test/java/one/jpro/platform/utils/test/CommandRunnerTests.java index 0b9ed4ab..427d5760 100644 --- a/internal/util/src/test/java/one/jpro/platform/internal/util/CommandRunnerTests.java +++ b/jpro-utils/src/test/java/one/jpro/platform/utils/test/CommandRunnerTests.java @@ -1,5 +1,7 @@ -package one.jpro.platform.internal.util; +package one.jpro.platform.utils.test; +import one.jpro.platform.utils.CommandRunner; +import one.jpro.platform.utils.PlatformUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; diff --git a/freeze-detector/src/test/java/one/jpro/platform/freezedetector/test/TestFreezeDetector.java b/jpro-utils/src/test/java/one/jpro/platform/utils/test/FreezeDetectorTests.java similarity index 86% rename from freeze-detector/src/test/java/one/jpro/platform/freezedetector/test/TestFreezeDetector.java rename to jpro-utils/src/test/java/one/jpro/platform/utils/test/FreezeDetectorTests.java index 04a63ca0..5e0ae14c 100644 --- a/freeze-detector/src/test/java/one/jpro/platform/freezedetector/test/TestFreezeDetector.java +++ b/jpro-utils/src/test/java/one/jpro/platform/utils/test/FreezeDetectorTests.java @@ -1,6 +1,6 @@ -package one.jpro.platform.freezedetector.test; +package one.jpro.platform.utils.test; -import one.jpro.platform.freezedetector.FreezeDetector; +import one.jpro.platform.utils.FreezeDetector; import org.junit.jupiter.api.Test; import org.testfx.framework.junit5.ApplicationTest; @@ -12,9 +12,9 @@ /** * Tests the freeze detector. * - * @author Besmir Beqiri + * @author Florian Kirmaier */ -public class TestFreezeDetector extends ApplicationTest { +public class FreezeDetectorTests extends ApplicationTest { @Test public void testFreezeDetector() throws InterruptedException { diff --git a/jpro-utils/src/test/java/one/jpro/platform/utils/test/OpenLinkTests.java b/jpro-utils/src/test/java/one/jpro/platform/utils/test/OpenLinkTests.java new file mode 100644 index 00000000..ff91ae94 --- /dev/null +++ b/jpro-utils/src/test/java/one/jpro/platform/utils/test/OpenLinkTests.java @@ -0,0 +1,98 @@ +package one.jpro.platform.utils.test; + +import one.jpro.platform.utils.OpenLink; +import one.jpro.platform.utils.PlatformUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import java.io.IOException; +import java.net.URI; + +import static org.mockito.Mockito.*; + +/** + * OpenLink tests. + * + * @author Besmir Beqiri + */ +public class OpenLinkTests { + + // Mocked static instances for Runtime and PlatformUtils + private static MockedStatic runtimeMockedStatic; + private static MockedStatic platformUtilsMockedStatic; + private static final String TEST_URL = "http://www.example.com"; + + private Runtime runtime; + + /** + * Initializes the static mocks before all tests run. + */ + @BeforeAll + public static void init() { + runtimeMockedStatic = mockStatic(Runtime.class); + platformUtilsMockedStatic = mockStatic(PlatformUtils.class); + } + + /** + * Cleans up the static mocks after all tests have run. + */ + @AfterAll + public static void cleanup() { + if (runtimeMockedStatic != null) { + runtimeMockedStatic.close(); + } + if (platformUtilsMockedStatic != null) { + platformUtilsMockedStatic.close(); + } + } + + /** + * Sets up the mock Runtime instance before each test. + */ + @BeforeEach + public void setup() { + // Create a mock Runtime instance + runtime = mock(Runtime.class); + // Stub Runtime.getRuntime() to return the mocked Runtime + runtimeMockedStatic.when(Runtime::getRuntime).thenReturn(runtime); + } + + @Test + public void testOpenURLOnMac() throws IOException { + when(PlatformUtils.isDesktop()).thenReturn(true); + when(PlatformUtils.isMac()).thenReturn(true); + when(PlatformUtils.isWindows()).thenReturn(false); + when(PlatformUtils.isLinux()).thenReturn(false); + + OpenLink.openURL(URI.create(TEST_URL).toURL()); + + verify(runtime).exec(new String[]{"open", TEST_URL}); + } + + @Test + public void testOpenURLOnWindows() throws IOException { + when(PlatformUtils.isDesktop()).thenReturn(true); + when(PlatformUtils.isMac()).thenReturn(false); + when(PlatformUtils.isWindows()).thenReturn(true); + when(PlatformUtils.isLinux()).thenReturn(false); + + OpenLink.openURL(URI.create(TEST_URL).toURL()); + + verify(runtime).exec(new String[]{"rundll32", "url.dll,FileProtocolHandler", TEST_URL}); + } + + @Test + public void testOpenURLOnLinux() throws IOException { + when(PlatformUtils.isDesktop()).thenReturn(true); + when(PlatformUtils.isMac()).thenReturn(false); + when(PlatformUtils.isWindows()).thenReturn(false); + when(PlatformUtils.isLinux()).thenReturn(true); + + OpenLink.openURL(URI.create(TEST_URL).toURL()); + + verify(runtime).exec(new String[]{"xdg-open", TEST_URL}); + } +} diff --git a/tree-showing/src/test/java/one/jpro/platform/treeshowing/TestTreeShowing.java b/jpro-utils/src/test/java/one/jpro/platform/utils/test/TreeShowingTests.java similarity index 62% rename from tree-showing/src/test/java/one/jpro/platform/treeshowing/TestTreeShowing.java rename to jpro-utils/src/test/java/one/jpro/platform/utils/test/TreeShowingTests.java index 928c21b9..4daf8600 100644 --- a/tree-showing/src/test/java/one/jpro/platform/treeshowing/TestTreeShowing.java +++ b/jpro-utils/src/test/java/one/jpro/platform/utils/test/TreeShowingTests.java @@ -1,32 +1,26 @@ -package one.jpro.platform.treeshowing; +package one.jpro.platform.utils.test; -import one.jpro.jmemorybuddy.JMemoryBuddy; -import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.scene.Group; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; +import one.jpro.jmemorybuddy.JMemoryBuddy; +import one.jpro.platform.utils.TreeShowing; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.testfx.framework.junit5.ApplicationTest; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -public class TestTreeShowing { - - - @BeforeAll - public static void startJavaFX() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - Platform.startup(latch::countDown); - latch.await(); - } +/** + * Tests for the {@link TreeShowing} utility class. + * + * @author Florian Kirmaier + */ +public class TreeShowingTests extends ApplicationTest { @Test public void simpleTest() { - inFX(() -> { + interact(() -> { Parent node = new Group(); BooleanProperty prop = TreeShowing.treeShowing(node); Assertions.assertFalse(prop.get()); @@ -35,7 +29,7 @@ public void simpleTest() { @Test public void memoryTest1() { - inFX(() -> JMemoryBuddy.memoryTest(checker -> { + interact(() -> JMemoryBuddy.memoryTest(checker -> { Parent node = new Group(); BooleanProperty prop = TreeShowing.treeShowing(node); @@ -46,7 +40,7 @@ public void memoryTest1() { @Test public void memoryTest2() { - inFX(() -> JMemoryBuddy.memoryTest(checker -> { + interact(() -> JMemoryBuddy.memoryTest(checker -> { Parent node = new Group(); BooleanProperty prop = TreeShowing.treeShowing(node); Scene scene = new Scene(node); @@ -63,11 +57,10 @@ public void memoryTest2() { })); } - - @Test + // @Test public void memoryTest3() { JMemoryBuddy.memoryTest(checker -> - inFX(() -> { + interact(() -> { Parent node = new Group(); BooleanProperty prop = TreeShowing.treeShowing(node); Scene scene = new Scene(node); @@ -87,26 +80,4 @@ public void memoryTest3() { checker.assertCollectable(stage); })); } - - public void inFX(Runnable r) { - CountDownLatch l = new CountDownLatch(1); - AtomicReference ex = new AtomicReference<>(); - Platform.runLater(() -> { - try { - r.run(); - } catch (Throwable e) { - ex.set(e); - } finally { - l.countDown(); - } - }); - try { - l.await(); - if (ex.get() != null) { - throw new RuntimeException(ex.get()); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } } diff --git a/settings.gradle b/settings.gradle index 41ca9ecd..7f2c7aae 100644 --- a/settings.gradle +++ b/settings.gradle @@ -26,10 +26,7 @@ include "jpro-webrtc" include "jpro-webrtc:example" include "jpro-sipjs" include "jpro-sipjs:example" -include "internal:openlink" -include "internal:util" -include "tree-showing" -include "freeze-detector" +include "jpro-utils" include "example" //include "ensemble:samples" //include "ensemble:site" diff --git a/tree-showing/build.gradle b/tree-showing/build.gradle deleted file mode 100644 index f4a3cda4..00000000 --- a/tree-showing/build.gradle +++ /dev/null @@ -1,15 +0,0 @@ -dependencies { - implementation "one.jpro:jmemorybuddy:$JMEMORYBUDDY_VERSION" -} - -publishing { - publications { - mavenJava(MavenPublication) { - pom { - name = 'Tree Showing' - description = 'A utility module for JavaFX applications that provides properties and methods to ' + - 'monitor and track the visibility status of nodes within the scene graph.' - } - } - } -} diff --git a/tree-showing/src/main/java/module-info.java b/tree-showing/src/main/java/module-info.java deleted file mode 100644 index ee0c50e2..00000000 --- a/tree-showing/src/main/java/module-info.java +++ /dev/null @@ -1,6 +0,0 @@ -module one.jpro.platform.treeshowing { - requires javafx.controls; - requires one.jpro.jmemorybuddy; - - exports one.jpro.platform.treeshowing; -} \ No newline at end of file