From 388ff8b2e0fea1e758ed5685d535e26b7a6131a9 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Fri, 2 Aug 2024 14:29:59 +0200 Subject: [PATCH 1/3] [New] allowing configuring shutdown hook The shutdown hook is hooked into the JVM and is executed when the JVM stops. This can be beneficial when the application is not managing pi4j's life cycle, but when an application wants to properly manage the life cycle, then this can be detrimental to the shutdown sequence of the application. This change allows such an application to disable this feature. The shutdown hook is still enabled by default, a future version might decide to disable it by default. --- .../java/com/pi4j/context/ContextBuilder.java | 28 +++++++++++++++++++ .../java/com/pi4j/context/ContextConfig.java | 6 ++++ .../context/impl/DefaultContextBuilder.java | 21 ++++++++++++++ .../com/pi4j/runtime/impl/DefaultRuntime.java | 20 +++++++------ 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/pi4j-core/src/main/java/com/pi4j/context/ContextBuilder.java b/pi4j-core/src/main/java/com/pi4j/context/ContextBuilder.java index 02084f58..3fc675df 100644 --- a/pi4j-core/src/main/java/com/pi4j/context/ContextBuilder.java +++ b/pi4j-core/src/main/java/com/pi4j/context/ContextBuilder.java @@ -147,6 +147,34 @@ default ContextBuilder setAutoInject(boolean autoInject){ return noAutoInject(); } + /** + *

enableShutdownHook.

+ * + * @return a {@link com.pi4j.context.ContextBuilder} object. + */ + ContextBuilder enableShutdownHook(); + + /** + *

disableShutdownHook.

+ * + * @return a {@link com.pi4j.context.ContextBuilder} object. + */ + ContextBuilder disableShutdownHook(); + + /** + *

setShutdownHook.

+ * + * @param enableShutdownHook a boolean. + * + * @return a {@link com.pi4j.context.ContextBuilder} object. + */ + default ContextBuilder setShutdownHook(boolean enableShutdownHook) { + if (enableShutdownHook) + return enableShutdownHook(); + else + return disableShutdownHook(); + } + /** *

toConfig.

* diff --git a/pi4j-core/src/main/java/com/pi4j/context/ContextConfig.java b/pi4j-core/src/main/java/com/pi4j/context/ContextConfig.java index 7f4f4b17..d4875e72 100644 --- a/pi4j-core/src/main/java/com/pi4j/context/ContextConfig.java +++ b/pi4j-core/src/main/java/com/pi4j/context/ContextConfig.java @@ -86,6 +86,12 @@ default Collection getPlatforms(){ * @return a boolean. */ boolean autoInject(); + /** + *

enableShutdownHook.

+ * + * @return a boolean. + */ + boolean enableShutdownHook(); /** *

getAutoInject.

* diff --git a/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java b/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java index 1316376a..6fd93f72 100644 --- a/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java +++ b/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java @@ -54,6 +54,7 @@ public class DefaultContextBuilder implements ContextBuilder { protected boolean autoDetectPlatforms = false; protected boolean autoDetectProviders = false; protected boolean autoInject = false; + protected boolean enableShutdownHook = true; // default platform identifier protected String defaultPlatformId = null; @@ -157,6 +158,20 @@ public ContextBuilder noAutoInject() { return this; } + /** {@inheritDoc} */ + @Override + public ContextBuilder enableShutdownHook() { + this.enableShutdownHook = true; + return this; + } + + /** {@inheritDoc} */ + @Override + public ContextBuilder disableShutdownHook() { + this.enableShutdownHook = false; + return this; + } + /** {@inheritDoc} */ @Override public ContextBuilder property(String key, String value){ @@ -258,11 +273,17 @@ public String defaultPlatform() { public boolean autoDetectMockPlugins() { return builder.autoDetectMockPlugins; } + @Override public boolean autoDetectPlatforms() { return builder.autoDetectPlatforms; } + @Override + public boolean enableShutdownHook() { + return builder.enableShutdownHook; + } + @Override public boolean autoInject() { return builder.autoInject; } diff --git a/pi4j-core/src/main/java/com/pi4j/runtime/impl/DefaultRuntime.java b/pi4j-core/src/main/java/com/pi4j/runtime/impl/DefaultRuntime.java index 1dffb9c0..2eb82817 100644 --- a/pi4j-core/src/main/java/com/pi4j/runtime/impl/DefaultRuntime.java +++ b/pi4j-core/src/main/java/com/pi4j/runtime/impl/DefaultRuntime.java @@ -110,15 +110,17 @@ private DefaultRuntime(Context context) { // listen for shutdown to properly clean up // TODO :: ADD PI4J INTERNAL SHUTDOWN CALLBACKS/EVENTS - java.lang.Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - // shutdown Pi4J - if (!isShutdown) - shutdown(); - } catch (Exception e) { - logger.error("Failed to shutdown Pi4J runtime", e); - } - }, "pi4j-shutdown")); + if (this.context.config().enableShutdownHook()) { + java.lang.Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + // shutdown Pi4J + if (!isShutdown) + shutdown(); + } catch (Exception e) { + logger.error("Failed to shutdown Pi4J runtime", e); + } + }, "pi4j-shutdown")); + } } /** From 1535055d417d58a4295b629ef6adc546f732a067 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Mon, 5 Aug 2024 08:16:13 +0200 Subject: [PATCH 2/3] [Minor] shutdown hook is disabled by default --- .../main/java/com/pi4j/context/impl/DefaultContextBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java b/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java index 6fd93f72..cd88e7f7 100644 --- a/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java +++ b/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java @@ -54,7 +54,7 @@ public class DefaultContextBuilder implements ContextBuilder { protected boolean autoDetectPlatforms = false; protected boolean autoDetectProviders = false; protected boolean autoInject = false; - protected boolean enableShutdownHook = true; + protected boolean enableShutdownHook = false; // default platform identifier protected String defaultPlatformId = null; From 9822afabed5b0c8cb1ebfcd7630410f72a14c5bc Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Mon, 5 Aug 2024 08:39:27 +0200 Subject: [PATCH 3/3] [Major] allow to load mocks on non-rpi systems This removes the Pi4J.newAutoContextAllowMocks() function, as we now detect these systems using: BoardInfoHelper.runningOnRaspberryPi() This is a breaking API change, but should satisfy people having to write special code to load the mock in tests, as this happens automatically now. If a mock is to be loaded on the Raspberry Pi, then one can still do this using the following code: Pi4J.newContextBuilder().autoDetectMockPlugins().build(); --- pi4j-core/src/main/java/com/pi4j/Pi4J.java | 18 ------------------ .../context/impl/DefaultContextBuilder.java | 3 ++- .../com/pi4j/test/context/ContextTest.java | 2 +- .../com/pi4j/test/io/i2c/I2CRawDataTest.java | 2 +- .../pi4j/test/io/i2c/I2CRegisterDataTest.java | 2 +- .../com/pi4j/test/io/spi/SpiRawDataTest.java | 2 +- .../pi4j/test/platform/AutoPlatformsTest.java | 2 +- .../pi4j/test/provider/AutoProvidersTest.java | 2 +- .../test/registry/RegistryGetIoInstance.java | 2 +- .../com/pi4j/test/registry/RegistryTest.java | 2 +- .../com/pi4j/test/runtime/RuntimeTest.java | 2 +- 11 files changed, 11 insertions(+), 28 deletions(-) diff --git a/pi4j-core/src/main/java/com/pi4j/Pi4J.java b/pi4j-core/src/main/java/com/pi4j/Pi4J.java index 5aa9e2f3..abafac7a 100644 --- a/pi4j-core/src/main/java/com/pi4j/Pi4J.java +++ b/pi4j-core/src/main/java/com/pi4j/Pi4J.java @@ -63,9 +63,6 @@ public static ContextBuilder newContextBuilder() { * state and lifecycle. This 'Context' instance will automatically * load all detected 'Platforms' and 'Providers' that are detected * in the application's class-path.

- * - *

This method does not allow mock plugins to be used. If this is required, e.g. for testing then use {@link #newAutoContextAllowMocks()}

- * * @return Context */ public static Context newAutoContext() { @@ -73,21 +70,6 @@ public static Context newAutoContext() { return newContextBuilder().autoDetect().build(); } - /** - *

Returns a new 'Context' instance which represents the Pi4J runtime - * state and lifecycle. This 'Context' instance will automatically - * load all detected 'Platforms' and 'Providers' that are detected - * in the application's class-path.

- * - *

In contrast to {@link #newAutoContext()} this method will allow mocks to be added to the runtime.

- * - * @return Context - */ - public static Context newAutoContextAllowMocks() { - logger.info("New auto context"); - return newContextBuilder().autoDetect().autoDetectMockPlugins().build(); - } - /** * Returns a new empty 'Context' instance which represents the Pi4J * runtime state and lifecycle. This empty 'Context' will not contain diff --git a/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java b/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java index cd88e7f7..de43b392 100644 --- a/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java +++ b/pi4j-core/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java @@ -25,6 +25,7 @@ * #L% */ +import com.pi4j.boardinfo.util.BoardInfoHelper; import com.pi4j.context.Context; import com.pi4j.context.ContextBuilder; import com.pi4j.context.ContextConfig; @@ -50,7 +51,7 @@ public class DefaultContextBuilder implements ContextBuilder { protected Logger logger = LoggerFactory.getLogger(DefaultContextBuilder.class); // auto detection flags - protected boolean autoDetectMockPlugins = false; + protected boolean autoDetectMockPlugins = !BoardInfoHelper.runningOnRaspberryPi(); protected boolean autoDetectPlatforms = false; protected boolean autoDetectProviders = false; protected boolean autoInject = false; diff --git a/pi4j-test/src/test/java/com/pi4j/test/context/ContextTest.java b/pi4j-test/src/test/java/com/pi4j/test/context/ContextTest.java index 10c04d6c..f7ba37a5 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/context/ContextTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/context/ContextTest.java @@ -51,7 +51,7 @@ public void beforeTest() throws Pi4JException { // An auto context includes AUTO-DETECT BINDINGS enabled // which will load all detected Pi4J extension libraries // (Platforms and Providers) in the class path - pi4j = Pi4J.newAutoContextAllowMocks(); + pi4j = Pi4J.newAutoContext(); } @AfterAll diff --git a/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRawDataTest.java b/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRawDataTest.java index 22bae0fe..71f0a953 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRawDataTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRawDataTest.java @@ -67,7 +67,7 @@ public void beforeTest() throws Pi4JException { // An auto context enabled AUTO-DETECT loading // which will load any detected Pi4J extension // libraries (Platforms and Providers) from the class path - pi4j = Pi4J.newAutoContextAllowMocks(); + pi4j = Pi4J.newAutoContext(); } @AfterEach diff --git a/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRegisterDataTest.java b/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRegisterDataTest.java index b432b41c..99fb3053 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRegisterDataTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRegisterDataTest.java @@ -68,7 +68,7 @@ public void beforeTest() throws Pi4JException { // An auto context enabled AUTO-DETECT loading // which will load any detected Pi4J extension // libraries (Platforms and Providers) from the class path - pi4j = Pi4J.newAutoContextAllowMocks(); + pi4j = Pi4J.newAutoContext(); } @AfterAll diff --git a/pi4j-test/src/test/java/com/pi4j/test/io/spi/SpiRawDataTest.java b/pi4j-test/src/test/java/com/pi4j/test/io/spi/SpiRawDataTest.java index 1b16ed1c..c3202b4c 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/io/spi/SpiRawDataTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/io/spi/SpiRawDataTest.java @@ -66,7 +66,7 @@ public void beforeTest() throws Pi4JException { // An auto context enabled AUTO-DETECT loading // which will load any detected Pi4J extension // libraries (Platforms and Providers) from the class path - pi4j = Pi4J.newAutoContextAllowMocks(); + pi4j = Pi4J.newAutoContext(); } @AfterEach diff --git a/pi4j-test/src/test/java/com/pi4j/test/platform/AutoPlatformsTest.java b/pi4j-test/src/test/java/com/pi4j/test/platform/AutoPlatformsTest.java index 94d9f6b0..fe844f34 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/platform/AutoPlatformsTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/platform/AutoPlatformsTest.java @@ -48,7 +48,7 @@ public void beforeTest() throws Pi4JException { // An auto context includes AUTO-DETECT BINDINGS enabled // which will load all detected Pi4J extension libraries // (Platforms and Providers) in the class path - pi4j = Pi4J.newAutoContextAllowMocks(); + pi4j = Pi4J.newAutoContext(); } @AfterAll diff --git a/pi4j-test/src/test/java/com/pi4j/test/provider/AutoProvidersTest.java b/pi4j-test/src/test/java/com/pi4j/test/provider/AutoProvidersTest.java index 02410476..c18bf9a6 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/provider/AutoProvidersTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/provider/AutoProvidersTest.java @@ -51,7 +51,7 @@ public void beforeTest() { // An auto context includes AUTO-DETECT BINDINGS enabled // which will load all detected Pi4J extension libraries // (Platforms and Providers) in the class path - pi4j = Pi4J.newAutoContextAllowMocks(); + pi4j = Pi4J.newAutoContext(); } @AfterAll diff --git a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstance.java b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstance.java index 1c1c5859..96d1bad8 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstance.java +++ b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstance.java @@ -53,7 +53,7 @@ public void beforeTest() throws Pi4JException { // An auto context includes AUTO-DETECT BINDINGS enabled // which will load all detected Pi4J extension libraries // (Platforms and Providers) in the class path - pi4j = Pi4J.newAutoContextAllowMocks(); + pi4j = Pi4J.newAutoContext(); } @AfterAll diff --git a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryTest.java b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryTest.java index c7c9ba51..92fcf70d 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryTest.java @@ -52,7 +52,7 @@ public void beforeTest() throws Pi4JException { // An auto context includes AUTO-DETECT BINDINGS enabled // which will load all detected Pi4J extension libraries // (Platforms and Providers) in the class path - pi4j = Pi4J.newAutoContextAllowMocks(); + pi4j = Pi4J.newAutoContext(); } @AfterAll diff --git a/pi4j-test/src/test/java/com/pi4j/test/runtime/RuntimeTest.java b/pi4j-test/src/test/java/com/pi4j/test/runtime/RuntimeTest.java index 9c1d58ce..32988406 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/runtime/RuntimeTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/runtime/RuntimeTest.java @@ -52,7 +52,7 @@ public void testRuntimeShutdownEvents() throws Pi4JException { // An auto context includes AUTO-DETECT BINDINGS enabled // which will load all detected Pi4J extension libraries // (Platforms and Providers) in the class path - Context pi4j = Pi4J.newAutoContextAllowMocks(); + Context pi4j = Pi4J.newAutoContext(); logger.info("-------------------------------------------------"); logger.info("Pi4J CONTEXT ");