From ed13a2e936b95f8c37e3d9776952a604c67fb3d6 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Sun, 8 Jan 2023 21:07:48 +0100 Subject: [PATCH 01/11] Switch to latest openbase type version v1.4. Implement detection of redundant launcher execution. Implement LauncherRemote. Setup launcher as controller again and start controller service. --- .../jul/pattern/launch/AbstractLauncher.java | 472 ++++++++++-------- .../jul/pattern/launch/LauncherRemote.kt | 6 + versions.properties | 2 +- 3 files changed, 258 insertions(+), 222 deletions(-) create mode 100644 module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/LauncherRemote.kt diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java index c8e44ccb9..cc47a2d33 100644 --- a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -29,11 +29,11 @@ import org.openbase.jps.core.JPService; import org.openbase.jps.exception.JPNotAvailableException; import org.openbase.jul.communication.controller.AbstractIdentifiableController; -import org.openbase.jul.communication.controller.RPCUtils; import org.openbase.jul.communication.iface.RPCServer; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.*; import org.openbase.jul.exception.printer.ExceptionPrinter; +import org.openbase.jul.extension.protobuf.ClosableDataBuilder; import org.openbase.jul.extension.type.processing.ScopeProcessor; import org.openbase.jul.iface.Launchable; import org.openbase.jul.iface.VoidInitializable; @@ -44,9 +44,13 @@ import org.openbase.jul.schedule.FutureProcessor; import org.openbase.jul.schedule.GlobalCachedExecutorService; import org.openbase.jul.schedule.SyncObject; -import org.openbase.type.domotic.state.ActivationStateType.ActivationState; +import org.openbase.type.domotic.state.ConnectionStateType.ConnectionState.State; +import org.openbase.type.execution.LauncherDataType.LauncherData; +import org.openbase.type.execution.LauncherDataType.LauncherData.Builder; +import org.openbase.type.execution.LauncherDataType.LauncherData.LauncherState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import java.io.File; import java.lang.reflect.InvocationTargetException; import java.util.*; @@ -59,21 +63,25 @@ * * @author Divine Threepwood */ -public abstract class AbstractLauncher extends AbstractIdentifiableController implements Launcher, VoidInitializable, NameProvider { - - protected final Logger logger = LoggerFactory.getLogger(getClass()); +public abstract class +AbstractLauncher extends AbstractIdentifiableController implements Launcher, VoidInitializable, NameProvider { public static final long LAUNCHER_TIMEOUT = 60000; public static final String SCOPE_PREFIX_LAUNCHER = ScopeProcessor.COMPONENT_SEPARATOR + "launcher"; - + private static final List> waitingTaskList = new ArrayList<>(); + private static final SyncObject VERIFICATION_STACK_LOCK = new SyncObject("VerificationStackLock"); + private static final SyncObject ERROR_STACK_LOCK = new SyncObject("ErrorStackLock"); + private static final SyncObject WAITING_TASK_LIST_LOCK = new SyncObject("WaitingStopLock"); + private static MultiException.ExceptionStack errorExceptionStack = null; + private static MultiException.ExceptionStack verificationExceptionStack = null; + protected final Logger logger = LoggerFactory.getLogger(getClass()); private final Class launchableClass; + private final SyncObject LAUNCHER_LOCK = new SyncObject(this); private final Class applicationClass; private L launchable; private long launchTime = -1; - private LauncherState state; private boolean verified; private VerificationFailedException verificationFailedException; - private Future launcherTask; /** @@ -88,209 +96,11 @@ public abstract class AbstractLauncher extends AbstractIde * @throws org.openbase.jul.exception.InstantiationException */ public AbstractLauncher(final Class applicationClass, final Class launchableClass) throws InstantiationException { - super(ActivationState.newBuilder()); + super(LauncherData.newBuilder()); this.launchableClass = launchableClass; this.applicationClass = applicationClass; } - @Override - public void init() throws InitializationException, InterruptedException { - super.init(SCOPE_PREFIX_LAUNCHER + ScopeProcessor.COMPONENT_SEPARATOR + ScopeProcessor.convertIntoValidScopeComponent(getName())); - } - - - /** - * This method can be overwritten to identify a core launcher. - * This means that if the start of this launcher fails the whole launching - * process will be stopped. - * - * @return false, can be overwritten to return true - */ - public boolean isCoreLauncher() { - return false; - } - - public L getLaunchable() { - return launchable; - } - - /** - * Method returns the application name. - *

- * By default the application name is the name of the given application class name. - * - * @return the name as string. - */ - @Override - public String getName() { - return generateName(); - } - - /** - * Method creates a launchable instance without any arguments.. In case the launchable needs arguments you can overwrite this method and instantiate the launchable by ourself. - * - * @return the new instantiated launchable. - * - * @throws CouldNotPerformException is thrown in case the launchable could not properly be instantiated. - */ - protected L instantiateLaunchable() throws CouldNotPerformException { - try { - return launchableClass.getConstructor().newInstance(); - } catch (java.lang.InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) { - throw new CouldNotPerformException("Could not load launchable class!", ex); - } - } - - // Load application specific java properties. - protected abstract void loadProperties(); - - /** - * Method verifies a running application. - * - * @throws VerificationFailedException is thrown if the application is started with any restrictions. - * @throws InterruptedException is thrown if the verification process is externally interrupted. - */ - protected void verify() throws VerificationFailedException, InterruptedException { - // overwrite for verification. - } - - private final SyncObject LAUNCHER_LOCK = new SyncObject(this); - - public enum LauncherState { - - INITIALIZING, - LAUNCHING, - RUNNING, - STOPPING, - STOPPED, - ERROR, - INTERRUPTED - } - - private void setState(final LauncherState state) { - this.state = state; - } - - @Override - public Future launch() { - - if (launcherTask != null && !launcherTask.isDone()) { - return FutureProcessor.canceledFuture(Void.class, new InvalidStateException("Could not launch " + getName() + "! Application still running!")); - } - - launcherTask = GlobalCachedExecutorService.submit(() -> { - synchronized (LAUNCHER_LOCK) { - setState(LauncherState.INITIALIZING); - launchable = instantiateLaunchable(); - try { - launchable.init(); - setState(LauncherState.LAUNCHING); - launchable.activate(); - launchTime = System.currentTimeMillis(); - setState(LauncherState.RUNNING); - try { - verify(); - verified = true; - } catch (VerificationFailedException ex) { - verified = false; - verificationFailedException = ex; - } - } catch (InterruptedException ex) { - setState(LauncherState.INTERRUPTED); - return null; - } catch (Exception ex) { - setState(LauncherState.ERROR); - launchable.shutdown(); - if (!ExceptionProcessor.isCausedBySystemShutdown(ex)) { - ExceptionPrinter.printHistoryAndReturnThrowable(new CouldNotPerformException("Could not launch " + getName(), ex), logger); - } - } - return null; - } - }); - return launcherTask; - } - - @Override - public void relaunch() throws CouldNotPerformException, InterruptedException { - synchronized (LAUNCHER_LOCK) { - stop(); - try { - launch().get(); - } catch (ExecutionException | CancellationException ex) { - throw new CouldNotPerformException(ex); - } - } - } - - @Override - public void stop() { - - interruptBoot(); - - synchronized (LAUNCHER_LOCK) { - setState(LauncherState.STOPPING); - if (launchable != null) { - launchable.shutdown(); - } - setState(LauncherState.STOPPED); - } - } - - /** - * Method cancels the boot process of this launcher. - */ - private void interruptBoot() { - if (isBooting()) { - launcherTask.cancel(true); - } - } - - /** - * @return true if the launcher is currently booting. - */ - private boolean isBooting() { - return launcherTask != null && !launcherTask.isDone(); - } - - @Override - public void shutdown() { - stop(); - super.shutdown(); - } - - @Override - public long getUpTime() { - if (launchTime < 0) { - return 0; - } - return (System.currentTimeMillis() - launchTime); - } - - @Override - public long getLaunchTime() { - return launchTime; - } - - @Override - public boolean isVerified() { - return verified; - } - - public VerificationFailedException getVerificationFailedCause() { - return verificationFailedException; - } - - public Future getLauncherTask() { - return launcherTask; - } - - private static MultiException.ExceptionStack errorExceptionStack = null; - private static MultiException.ExceptionStack verificationExceptionStack = null; - - private static final List> waitingTaskList = new ArrayList<>(); - - private static void loadCustomLoggerSettings() throws CouldNotPerformException { // assume SLF4J is bound to logback in the current environment final LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); @@ -401,7 +211,8 @@ public static void main(final Class application, final String[] args, final C for (final Class launcherClass : launchers) { try { launcherMap.put(launcherClass, launcherClass.getConstructor().newInstance()); - } catch (java.lang.InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) { + } catch (java.lang.InstantiationException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException ex) { errorExceptionStack = MultiException.push(application, new CouldNotPerformException("Could not load launcher class!", ex), errorExceptionStack); } } @@ -434,7 +245,7 @@ public static void main(final Class application, final String[] args, final C try { if (JPService.getProperty(JPPrintLauncher.class).getValue()) { if (launcherMap.isEmpty()) { - System.out.println(generateName() + " does not provide any launcher!"); + System.out.println(generateAppName() + " does not provide any launcher!"); System.exit(255); } System.out.println("Available launcher:"); @@ -453,12 +264,13 @@ public static void main(final Class application, final String[] args, final C ExceptionPrinter.printHistory("Could not check if launcher should be printed.", ex, logger); } - logger.info("Start " + generateName() + "..."); + logger.info("Start " + generateAppName() + "..."); for (final Entry, AbstractLauncher> launcherEntry : new HashSet<>(launcherMap.entrySet())) { // check if launcher was excluded boolean exclude = false; + try { //filter excluded launcher for (String exclusionPatter : JPService.getProperty(JPExcludeLauncher.class).getValue()) { @@ -469,6 +281,7 @@ public static void main(final Class application, final String[] args, final C } catch (JPNotAvailableException ex) { ExceptionPrinter.printHistory("Could not process launcher exclusion!", ex, logger); } + if (exclude) { logger.info(launcherEntry.getKey().getSimpleName() + " excluded from execution."); launcherMap.remove(launcherEntry.getKey()); @@ -553,17 +366,13 @@ public static void main(final Class application, final String[] args, final C Thread.currentThread().interrupt(); // print a summary containing the exceptions - printSummary(application, logger, generateName() + " caught shutdown signal during startup phase!"); + printSummary(application, logger, generateAppName() + " caught shutdown signal during startup phase!"); return; } - printSummary(application, logger, generateName() + " was started with restrictions!"); + printSummary(application, logger, generateAppName() + " was started with restrictions!"); } - private static final SyncObject VERIFICATION_STACK_LOCK = new SyncObject("VerificationStackLock"); - private static final SyncObject ERROR_STACK_LOCK = new SyncObject("ErrorStackLock"); - private static final SyncObject WAITING_TASK_LIST_LOCK = new SyncObject("WaitingStopLock"); - private static void pushToVerificationExceptionStack(Object source, Exception ex) { synchronized (VERIFICATION_STACK_LOCK) { verificationExceptionStack = MultiException.push(source, ex, verificationExceptionStack); @@ -607,7 +416,7 @@ private static void forceStopLauncher(final Map errorMessage, exceptionStack); - logger.info(generateName()+" successfully started."); + logger.info(generateAppName() + " successfully started."); } catch (MultiException ex) { ExceptionPrinter.printHistory(ex, logger); } } + @Override + public void init() throws InitializationException, InterruptedException { + super.init(SCOPE_PREFIX_LAUNCHER + ScopeProcessor.COMPONENT_SEPARATOR + JPService.getApplicationName() + ScopeProcessor.COMPONENT_SEPARATOR + ScopeProcessor.convertIntoValidScopeComponent(getName())); + + try { + verifyNonRedundantExecution(); + } catch (VerificationFailedException e) { + ExceptionPrinter.printHistoryAndExit("Application startup skipped!", e, logger); + } + } + + /** + * This method can be overwritten to identify a core launcher. + * This means that if the start of this launcher fails the whole launching + * process will be stopped. + * + * @return false, can be overwritten to return true + */ + public boolean isCoreLauncher() { + return false; + } + + public L getLaunchable() { + return launchable; + } + + /** + * Method returns the name of this launcher. + * + * @return the name as string. + */ + @Override + public String getName() { + return getClass().getSimpleName().replace("Launcher", ""); + } + + /** + * Method creates a launchable instance without any arguments.. In case the launchable needs arguments you can overwrite this method and instantiate the launchable by ourself. + * + * @return the new instantiated launchable. + * + * @throws CouldNotPerformException is thrown in case the launchable could not properly be instantiated. + */ + protected L instantiateLaunchable() throws CouldNotPerformException { + try { + return launchableClass.getConstructor().newInstance(); + } catch (java.lang.InstantiationException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException ex) { + throw new CouldNotPerformException("Could not load launchable class!", ex); + } + } + + // Load application specific java properties. + protected abstract void loadProperties(); + + /** + * Method verifies a running application. + * + * @throws VerificationFailedException is thrown if the application is started with any restrictions. + * @throws InterruptedException is thrown if the verification process is externally interrupted. + */ + protected void verify() throws VerificationFailedException, InterruptedException { + // overwrite for verification. + } + + private void setState(final LauncherState state) { + try (ClosableDataBuilder dataBuilder = getDataBuilder(this)) { + dataBuilder.getInternalBuilder().setLauncherState(state); + } catch (Exception e) { + ExceptionPrinter.printHistory("Could not apply state change!", e, logger); + } + } + + public void verifyNonRedundantExecution() throws VerificationFailedException { + // verify that launcher was not already externally started + try { + final LauncherRemote launcherRemote = new LauncherRemote(); + launcherRemote.init(getScope()); + try { + launcherRemote.activate(); + launcherRemote.waitForConnectionState(State.CONNECTED, 1000); + throw new VerificationFailedException("Redundant execution of Launcher[" + getName() + "] detected!"); + } catch (org.openbase.jul.exception.TimeoutException e) { + // this is the default since to other instant should be launched + } finally { + launcherRemote.shutdown(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (VerificationFailedException e) { + throw e; + } catch (CouldNotPerformException e) { + ExceptionPrinter.printHistory("Could not properly detect redundant launcher!", e, logger); + } + } + + @Override + public Future launch() { + + if (launcherTask != null && !launcherTask.isDone()) { + return FutureProcessor.canceledFuture(Void.class, new InvalidStateException("Could not launch " + getName() + "! Application still running!")); + } + + launcherTask = GlobalCachedExecutorService.submit(() -> { + + try { + init(); + activate(); + } catch (CouldNotPerformException ex) { + ExceptionPrinter.printHistory("Could not activate Launcher[" + getName() + "]!", ex, logger); + } catch (InterruptedException e) { + throw e; + } + + synchronized (LAUNCHER_LOCK) { + setState(LauncherState.INITIALIZING); + launchable = instantiateLaunchable(); + try { + launchable.init(); + setState(LauncherState.LAUNCHING); + + launchable.activate(); + launchTime = System.currentTimeMillis(); + setState(LauncherState.RUNNING); + try { + verify(); + verified = true; + } catch (VerificationFailedException ex) { + verified = false; + verificationFailedException = ex; + } + } catch (InterruptedException ex) { + setState(LauncherState.STOPPING); + return null; + } catch (Exception ex) { + setState(LauncherState.ERROR); + launchable.shutdown(); + if (!ExceptionProcessor.isCausedBySystemShutdown(ex)) { + ExceptionPrinter.printHistoryAndReturnThrowable(new CouldNotPerformException("Could not launch " + getName(), ex), logger); + } + } + return null; + } + }); + return launcherTask; + } + + @Override + public void relaunch() throws CouldNotPerformException, InterruptedException { + synchronized (LAUNCHER_LOCK) { + stop(); + try { + launch().get(); + } catch (ExecutionException | CancellationException ex) { + throw new CouldNotPerformException(ex); + } + } + } + + @Override + public void stop() { + + interruptBoot(); + + synchronized (LAUNCHER_LOCK) { + setState(LauncherState.STOPPING); + if (launchable != null) { + launchable.shutdown(); + } + setState(LauncherState.STOPPED); + } + } + + /** + * Method cancels the boot process of this launcher. + */ + private void interruptBoot() { + if (isBooting()) { + launcherTask.cancel(true); + } + } + + /** + * @return true if the launcher is currently booting. + */ + private boolean isBooting() { + return launcherTask != null && !launcherTask.isDone(); + } + + @Override + public void shutdown() { + stop(); + super.shutdown(); + } + + @Override + public long getUpTime() { + if (launchTime < 0) { + return 0; + } + return (System.currentTimeMillis() - launchTime); + } + + @Override + public long getLaunchTime() { + return launchTime; + } + + @Override + public boolean isVerified() { + return verified; + } + + public VerificationFailedException getVerificationFailedCause() { + return verificationFailedException; + } + + public Future getLauncherTask() { + return launcherTask; + } + @Override public void registerMethods(RPCServer server) { // currently, the launcher does not support any rpc methods. diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/LauncherRemote.kt b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/LauncherRemote.kt new file mode 100644 index 000000000..efe6e4514 --- /dev/null +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/LauncherRemote.kt @@ -0,0 +1,6 @@ +package org.openbase.jul.pattern.launch + +import org.openbase.jul.communication.controller.AbstractIdentifiableRemote +import org.openbase.type.execution.LauncherDataType.LauncherData + +class LauncherRemote: AbstractIdentifiableRemote(LauncherData::class.java) diff --git a/versions.properties b/versions.properties index 6415601b0..7be7f57b0 100644 --- a/versions.properties +++ b/versions.properties @@ -124,7 +124,7 @@ version.org.openjfx..javafx-controls=17 ## unused version.org.openjfx..javafx-base=17 -version.org.openbase..type=[1.3,1.4-alpha) +version.org.openbase..type=[1.4,1.5-alpha) version.org.openbase..jps=[3.6,3.7-alpha) ## # available=3.1.1 From e08431de575351f3f1ddd52e62e403c36181af59 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Sun, 8 Jan 2023 21:15:10 +0100 Subject: [PATCH 02/11] fix type --- .../java/org/openbase/jul/pattern/launch/AbstractLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java index cc47a2d33..8940f5ce4 100644 --- a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java @@ -531,7 +531,7 @@ public void verifyNonRedundantExecution() throws VerificationFailedException { launcherRemote.waitForConnectionState(State.CONNECTED, 1000); throw new VerificationFailedException("Redundant execution of Launcher[" + getName() + "] detected!"); } catch (org.openbase.jul.exception.TimeoutException e) { - // this is the default since to other instant should be launched + // this is the default since no other instant should be launched yet. } finally { launcherRemote.shutdown(); } From a9d027edd3a283fa67564afa11453628d302301e Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Mon, 9 Jan 2023 18:34:43 +0100 Subject: [PATCH 03/11] Move MqttIntegrationTest base class into a dedecated test package. Implement launcher test. --- .../communication/controller/build.gradle.kts | 5 +- .../AbstractControllerServerTest.java | 1 + .../controller/AbstractRemoteClientTest.java | 1 + .../ConfigurableControllerAndRemoteTest.java | 1 + .../controller/FutureCancelTest.java | 1 + .../controller/MqttIntegrationTest.java | 76 --------------- .../communication/mqtttest/build.gradle.kts | 29 ++++++ .../mqtt/test/MqttIntegrationTest.kt | 94 +++++++++++++++++++ .../exception/printer/ExceptionPrinter.java | 4 +- .../extension/protobuf/BuilderSyncSetup.java | 9 +- module/pattern/launch/build.gradle.kts | 1 + .../jul/pattern/launch/AbstractLauncher.java | 24 +++-- settings.gradle.kts | 2 + 13 files changed, 156 insertions(+), 92 deletions(-) delete mode 100644 module/communication/controller/src/test/java/org/openbase/jul/communication/controller/MqttIntegrationTest.java create mode 100644 module/communication/mqtttest/build.gradle.kts create mode 100644 module/communication/mqtttest/src/main/kotlin/org/openbase/jul/communication/mqtt/test/MqttIntegrationTest.kt diff --git a/module/communication/controller/build.gradle.kts b/module/communication/controller/build.gradle.kts index aa4bcc9ef..e1c39cd21 100644 --- a/module/communication/controller/build.gradle.kts +++ b/module/communication/controller/build.gradle.kts @@ -18,10 +18,7 @@ dependencies { api(project(":jul.interface")) api(project(":jul.schedule")) api(project(":jul.pattern.controller")) - testImplementation("org.testcontainers:junit-jupiter:_") { - exclude(group = "junit", module = "junit") - } - testImplementation("io.quarkus:quarkus-junit4-mock:_") // required as long as testcontainers depends on junit4 + testApi(project(":jul.communication.mqtt.test")) } description = "JUL Extension Controller" diff --git a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/AbstractControllerServerTest.java b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/AbstractControllerServerTest.java index 85cebf093..54fd894dc 100644 --- a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/AbstractControllerServerTest.java +++ b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/AbstractControllerServerTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.function.Executable; import org.openbase.jul.communication.iface.RPCServer; +import org.openbase.jul.communication.mqtt.test.MqttIntegrationTest; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.FatalImplementationErrorException; import org.openbase.jul.exception.InstantiationException; diff --git a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/AbstractRemoteClientTest.java b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/AbstractRemoteClientTest.java index e7ccbd8dc..dc3cb649f 100644 --- a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/AbstractRemoteClientTest.java +++ b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/AbstractRemoteClientTest.java @@ -29,6 +29,7 @@ import org.openbase.jul.communication.controller.AbstractControllerServerTest.AbstractControllerServerImpl; import org.openbase.jul.communication.controller.AbstractControllerServerTest.AbstractRemoteClientImpl; import org.openbase.jul.communication.iface.RPCServer; +import org.openbase.jul.communication.mqtt.test.MqttIntegrationTest; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.TimeoutException; diff --git a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/ConfigurableControllerAndRemoteTest.java b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/ConfigurableControllerAndRemoteTest.java index b32241681..aec773370 100644 --- a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/ConfigurableControllerAndRemoteTest.java +++ b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/ConfigurableControllerAndRemoteTest.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.openbase.jul.communication.iface.RPCServer; +import org.openbase.jul.communication.mqtt.test.MqttIntegrationTest; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.type.communication.ScopeType.Scope; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; diff --git a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/FutureCancelTest.java b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/FutureCancelTest.java index 4a8f58a7e..27f51342a 100644 --- a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/FutureCancelTest.java +++ b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/FutureCancelTest.java @@ -32,6 +32,7 @@ import org.openbase.jul.communication.iface.RPCServer; import org.openbase.jul.communication.mqtt.CommunicatorFactoryImpl; import org.openbase.jul.communication.mqtt.DefaultCommunicatorConfig; +import org.openbase.jul.communication.mqtt.test.MqttIntegrationTest; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.extension.type.processing.ScopeProcessor; import org.openbase.jul.iface.Requestable; diff --git a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/MqttIntegrationTest.java b/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/MqttIntegrationTest.java deleted file mode 100644 index a9d35e95a..000000000 --- a/module/communication/controller/src/test/java/org/openbase/jul/communication/controller/MqttIntegrationTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.openbase.jul.communication.controller; - -/*- - * #%L - * JUL Extension Controller - * %% - * Copyright (C) 2015 - 2022 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.openbase.jps.core.JPService; -import org.openbase.jps.exception.JPServiceException; -import org.openbase.jul.communication.jp.JPComHost; -import org.openbase.jul.communication.jp.JPComPort; -import org.openbase.jul.communication.mqtt.SharedMqttClient; -import org.testcontainers.containers.BindMode; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.utility.DockerImageName; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.Duration; -import java.util.Arrays; - -public class MqttIntegrationTest { - - public static final int port = 1883; - public static Path mosquittoConfig; - public static GenericContainer broker; - - @BeforeAll - public static void setUpClass() throws JPServiceException, IOException { - mosquittoConfig = Files.createTempFile("mosquitto_", ".conf"); - Files.write(mosquittoConfig, Arrays.asList( - "allow_anonymous true", - "listener "+port) - ); - - broker = new GenericContainer<>(DockerImageName.parse("eclipse-mosquitto")) - .withExposedPorts(port) - .withFileSystemBind( - mosquittoConfig.toString(), - "/mosquitto/config/mosquitto.conf", - BindMode.READ_ONLY - ); - broker.withStartupTimeout(Duration.ofSeconds(30)).start(); - - JPService.registerProperty(JPComPort.class, broker.getFirstMappedPort()); - JPService.registerProperty(JPComHost.class, broker.getHost()); - JPService.setupJUnitTestMode(); - } - - @AfterAll - public static void tearDownClass() throws IOException { - SharedMqttClient.INSTANCE.waitForShutdown(); - broker.stop(); - Files.delete(mosquittoConfig); - } -} diff --git a/module/communication/mqtttest/build.gradle.kts b/module/communication/mqtttest/build.gradle.kts new file mode 100644 index 000000000..a1b564a04 --- /dev/null +++ b/module/communication/mqtttest/build.gradle.kts @@ -0,0 +1,29 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This project uses @Incubating APIs which are subject to change. + */ + +plugins { + id("org.openbase.jul") +} + +dependencies { + api(project(":jul.communication")) + api(project(":jul.schedule")) + api(project(":jul.extension.type.processing")) + api(project(":jul.communication.mqtt")) + api("com.hivemq:hivemq-mqtt-client:_") + api("org.testcontainers:junit-jupiter:_") { + exclude(group = "junit", module = "junit") + } + api("io.quarkus:quarkus-junit4-mock:_") + api(Testing.junit.jupiter) + api(Testing.junit.jupiter.api) +} + +description = "JUL Extension MQTT Test" + +java { + withJavadocJar() +} diff --git a/module/communication/mqtttest/src/main/kotlin/org/openbase/jul/communication/mqtt/test/MqttIntegrationTest.kt b/module/communication/mqtttest/src/main/kotlin/org/openbase/jul/communication/mqtt/test/MqttIntegrationTest.kt new file mode 100644 index 000000000..d2be17038 --- /dev/null +++ b/module/communication/mqtttest/src/main/kotlin/org/openbase/jul/communication/mqtt/test/MqttIntegrationTest.kt @@ -0,0 +1,94 @@ +package org.openbase.jul.communication.mqtt.test +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.TestInstance +import org.openbase.jps.core.JPService +import org.openbase.jps.exception.JPServiceException +import org.openbase.jul.communication.jp.JPComHost +import org.openbase.jul.communication.jp.JPComPort +import org.openbase.jul.communication.mqtt.SharedMqttClient.waitForShutdown +import org.testcontainers.containers.BindMode +import org.testcontainers.containers.GenericContainer +import org.testcontainers.utility.DockerImageName +import java.nio.file.Files +import java.nio.file.Path +import java.time.Duration +import java.util.* + +/*- + * #%L + * JUL Extension Controller + * %% + * Copyright (C) 2015 - 2021 openbase.org + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + + * */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +open class MqttIntegrationTest { + + companion object { + const val port = 1884 + var mosquittoConfig: Path? = null + var broker: GenericContainer<*>? = null + val configLock = Any() + } + + @BeforeAll + fun setupMqtt() { + synchronized(configLock) { + mosquittoConfig = Files.createTempFile("mosquitto_", ".conf") + Files.write( + mosquittoConfig, listOf( + "allow_anonymous true", + "listener " + port + ) + ) + GenericContainer(DockerImageName.parse("eclipse-mosquitto")) + .withExposedPorts(port) + .withFileSystemBind( + mosquittoConfig.toString(), + "/mosquitto/config/mosquitto.conf", + BindMode.READ_ONLY + ) + .apply { withStartupTimeout(Duration.ofSeconds(30)).start() } + .also { if (broker?.takeIf { it.containerId != null } != null) error("broker was already initialized!") } + .also { broker = it } + .also { setupProperties() } + } + } + + @AfterAll + fun tearDownMQTT() { + synchronized(configLock) { + waitForShutdown() + broker?.stop() + Files.delete(mosquittoConfig) + } + } + + @Throws(JPServiceException::class) + private fun setupProperties() { + JPService.reset() + JPService.registerProperty(JPComPort::class.java, broker!!.firstMappedPort) + JPService.registerProperty(JPComHost::class.java, broker!!.host) + setupCustomProperties() + JPService.setupJUnitTestMode() + } + + open fun setupCustomProperties() {} +} diff --git a/module/exception/src/main/java/org/openbase/jul/exception/printer/ExceptionPrinter.java b/module/exception/src/main/java/org/openbase/jul/exception/printer/ExceptionPrinter.java index bb90e62f8..d965a0d2b 100644 --- a/module/exception/src/main/java/org/openbase/jul/exception/printer/ExceptionPrinter.java +++ b/module/exception/src/main/java/org/openbase/jul/exception/printer/ExceptionPrinter.java @@ -36,6 +36,7 @@ import org.openbase.jul.exception.FatalImplementationErrorException; import org.openbase.jul.exception.MultiException; import org.openbase.jul.exception.MultiException.SourceExceptionEntry; +import org.openbase.jul.exception.ShutdownException; import org.slf4j.Logger; /** @@ -214,8 +215,7 @@ public static void printHistory(final String message, T th public static void printHistoryAndExit(final String message, T th, final Logger logger) { printHistory(new CouldNotPerformException(message, th), logger, LogLevel.ERROR); if (JPService.testMode()) { - assert false; - return; + throw new RuntimeException(new ShutdownException(message, th)); } exit(255); } diff --git a/module/extension/protobuf/src/main/java/org/openbase/jul/extension/protobuf/BuilderSyncSetup.java b/module/extension/protobuf/src/main/java/org/openbase/jul/extension/protobuf/BuilderSyncSetup.java index 9c4a27ef0..cb09ae31d 100644 --- a/module/extension/protobuf/src/main/java/org/openbase/jul/extension/protobuf/BuilderSyncSetup.java +++ b/module/extension/protobuf/src/main/java/org/openbase/jul/extension/protobuf/BuilderSyncSetup.java @@ -266,10 +266,17 @@ public void unlockWrite(final NotificationStrategy notificationStrategy) { } catch (InterruptedException ex) { Thread.currentThread().interrupt(); return; + } catch (NotInitializedException ex) { + // do nothing if service is not initialized yet } catch (CouldNotPerformException ex) { // only print error if the exception was not caused by a system shutdown. if (!ExceptionProcessor.isCausedBySystemShutdown(ex)) { - ExceptionPrinter.printHistory(new CouldNotPerformException("Could not inform builder holder about data update!", ex), logger, LogLevel.ERROR); + ExceptionPrinter.printHistory( + "Could not inform builder holder about data update!", + ex, + logger, + LogLevel.ERROR + ); } } } diff --git a/module/pattern/launch/build.gradle.kts b/module/pattern/launch/build.gradle.kts index 4c3caf59f..4bd9a76c0 100644 --- a/module/pattern/launch/build.gradle.kts +++ b/module/pattern/launch/build.gradle.kts @@ -11,6 +11,7 @@ plugins { dependencies { api(project(":jul.interface")) api(project(":jul.communication.controller")) + testApi(project(":jul.communication.mqtt.test")) } description = "JUL Pattern Launch" diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java index 8940f5ce4..f953846a1 100644 --- a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java @@ -553,17 +553,23 @@ public Future launch() { launcherTask = GlobalCachedExecutorService.submit(() -> { - try { - init(); - activate(); - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not activate Launcher[" + getName() + "]!", ex, logger); - } catch (InterruptedException e) { - throw e; - } - synchronized (LAUNCHER_LOCK) { + setState(LauncherState.INITIALIZING); + + try { + init(); + activate(); + } catch (CouldNotPerformException ex) { + ExceptionPrinter.printHistory("Could not activate Launcher[" + getName() + "]!", ex, logger); + } catch (RuntimeException e) { + setState(LauncherState.ERROR); + throw e; + } catch (InterruptedException e) { + setState(LauncherState.STOPPED); + throw e; + } + launchable = instantiateLaunchable(); try { launchable.init(); diff --git a/settings.gradle.kts b/settings.gradle.kts index 5242723f6..1ed8dc563 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,6 +12,7 @@ include(":jul.extension.type.storage") include(":jul.pattern.module") include(":jul.extension.type") include(":jul.communication.mqtt") +include(":jul.communication.mqtt.test") include(":jul.processing.xml") include(":jul.extension.protobuf") include(":jul.processing.json") @@ -44,6 +45,7 @@ project(":jul.extension.type.storage").projectDir = file("module/extension/type/ project(":jul.pattern.module").projectDir = file("module/pattern") project(":jul.extension.type").projectDir = file("module/extension/type") project(":jul.communication.mqtt").projectDir = file("module/communication/mqtt") +project(":jul.communication.mqtt.test").projectDir = file("module/communication/mqtttest") project(":jul.processing.xml").projectDir = file("module/processing/xml") project(":jul.extension.protobuf").projectDir = file("module/extension/protobuf") project(":jul.processing.json").projectDir = file("module/processing/json") From 9a7a2dfcc6883c9633bfbacbbe13c231e96ca963 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Mon, 9 Jan 2023 18:41:50 +0100 Subject: [PATCH 04/11] fix linter issue. --- .../jul/communication/mqtt/test/MqttIntegrationTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/communication/mqtttest/src/main/kotlin/org/openbase/jul/communication/mqtt/test/MqttIntegrationTest.kt b/module/communication/mqtttest/src/main/kotlin/org/openbase/jul/communication/mqtt/test/MqttIntegrationTest.kt index d2be17038..5f403a357 100644 --- a/module/communication/mqtttest/src/main/kotlin/org/openbase/jul/communication/mqtt/test/MqttIntegrationTest.kt +++ b/module/communication/mqtttest/src/main/kotlin/org/openbase/jul/communication/mqtt/test/MqttIntegrationTest.kt @@ -66,7 +66,8 @@ open class MqttIntegrationTest { BindMode.READ_ONLY ) .apply { withStartupTimeout(Duration.ofSeconds(30)).start() } - .also { if (broker?.takeIf { it.containerId != null } != null) error("broker was already initialized!") } + .also { if (broker?.takeIf { it.containerId != null } != null) + error("broker was already initialized!") } .also { broker = it } .also { setupProperties() } } From c71318bcf1457c3f209e9cf896766c03a47bb0ac Mon Sep 17 00:00:00 2001 From: Marian Pohling Date: Mon, 9 Jan 2023 20:40:39 +0100 Subject: [PATCH 05/11] Update module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java Co-authored-by: pLeminoq --- .../java/org/openbase/jul/pattern/launch/AbstractLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java index f953846a1..1c405f77c 100644 --- a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java @@ -71,7 +71,7 @@ private static final List> waitingTaskList = new ArrayList<>(); private static final SyncObject VERIFICATION_STACK_LOCK = new SyncObject("VerificationStackLock"); private static final SyncObject ERROR_STACK_LOCK = new SyncObject("ErrorStackLock"); - private static final SyncObject WAITING_TASK_LIST_LOCK = new SyncObject("WaitingStopLock"); + private static final SyncObject WAITING_TASK_LIST_LOCK = new SyncObject("WaitingTaskLock"); private static MultiException.ExceptionStack errorExceptionStack = null; private static MultiException.ExceptionStack verificationExceptionStack = null; protected final Logger logger = LoggerFactory.getLogger(getClass()); From 783a19fe0bdf9ea185621e1622a6041ca4d659cd Mon Sep 17 00:00:00 2001 From: Marian Pohling Date: Mon, 9 Jan 2023 20:47:22 +0100 Subject: [PATCH 06/11] Update module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java Co-authored-by: pLeminoq --- .../java/org/openbase/jul/pattern/launch/AbstractLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java index 1c405f77c..5a024d7e3 100644 --- a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java @@ -531,7 +531,7 @@ public void verifyNonRedundantExecution() throws VerificationFailedException { launcherRemote.waitForConnectionState(State.CONNECTED, 1000); throw new VerificationFailedException("Redundant execution of Launcher[" + getName() + "] detected!"); } catch (org.openbase.jul.exception.TimeoutException e) { - // this is the default since no other instant should be launched yet. + // this is the default since no other instance should be launched yet. } finally { launcherRemote.shutdown(); } From 11c35a70581239e696cff9afe4770fbb3b41fb2e Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Mon, 9 Jan 2023 20:47:06 +0100 Subject: [PATCH 07/11] apply api changes of AbstractLauncher as requested in the pr by renaming isBooting to isLauching and interruptBoot to interruptLaunch. --- .../jul/pattern/launch/AbstractLauncher.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java index 5a024d7e3..a5809e64f 100644 --- a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java @@ -402,7 +402,7 @@ private static void interruptLauncherBoot(final Map, AbstractLauncher> launcherEntryToStop : launcherMap.entrySet()) { - launcherEntryToStop.getValue().interruptBoot(); + launcherEntryToStop.getValue().interruptLaunch(); } } @@ -616,7 +616,7 @@ public void relaunch() throws CouldNotPerformException, InterruptedException { @Override public void stop() { - interruptBoot(); + interruptLaunch(); synchronized (LAUNCHER_LOCK) { setState(LauncherState.STOPPING); @@ -628,18 +628,18 @@ public void stop() { } /** - * Method cancels the boot process of this launcher. + * Method cancels the launch process of this launcher. */ - private void interruptBoot() { - if (isBooting()) { + private void interruptLaunch() { + if (isLaunching()) { launcherTask.cancel(true); } } /** - * @return true if the launcher is currently booting. + * @return true if the launcher is currently launching. */ - private boolean isBooting() { + private boolean isLaunching() { return launcherTask != null && !launcherTask.isDone(); } From 6ffa1a313d8fc6e29be485137a2bd76d7ed92f03 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Wed, 11 Jan 2023 01:17:24 +0100 Subject: [PATCH 08/11] fix deadlock in shutdown routine. Improve launcher exception handling. Make sure CloseableReadLockWrapper and CloseableWriteLockWrapper are always following an interruptible locking strategy. --- .../exception/InitializationException.java | 6 +- .../exception/RedundantExecutionException.kt | 5 + .../ShutdownInProgressException.java | 11 ++ .../exception/printer/ExceptionPrinter.java | 24 +--- .../jul/pattern/launch/AbstractLauncher.java | 123 +++++++++--------- .../jul/schedule/CloseableLockProvider.java | 3 +- .../schedule/CloseableReadLockWrapper.java | 7 +- .../schedule/CloseableWriteLockWrapper.java | 7 +- 8 files changed, 102 insertions(+), 84 deletions(-) create mode 100644 module/exception/src/main/java/org/openbase/jul/exception/RedundantExecutionException.kt diff --git a/module/exception/src/main/java/org/openbase/jul/exception/InitializationException.java b/module/exception/src/main/java/org/openbase/jul/exception/InitializationException.java index e815c9f9b..3d1055055 100644 --- a/module/exception/src/main/java/org/openbase/jul/exception/InitializationException.java +++ b/module/exception/src/main/java/org/openbase/jul/exception/InitializationException.java @@ -50,10 +50,10 @@ public InitializationException(Object context, Throwable cause) { /** * Creates a new InitializationException instance. * - * @param context the class which could not be initialized. + * @param clazz the class which could not be initialized. * @param cause the reason why the initialization failed. */ - public InitializationException(Class context, Throwable cause) { - super("Could not initialize " + context + "!", cause); + public InitializationException(Class clazz, Throwable cause) { + super("Could not initialize " + clazz.getSimpleName() + "!", cause); } } diff --git a/module/exception/src/main/java/org/openbase/jul/exception/RedundantExecutionException.kt b/module/exception/src/main/java/org/openbase/jul/exception/RedundantExecutionException.kt new file mode 100644 index 000000000..7510332af --- /dev/null +++ b/module/exception/src/main/java/org/openbase/jul/exception/RedundantExecutionException.kt @@ -0,0 +1,5 @@ +package org.openbase.jul.exception + +class RedundantExecutionException( + context: String +): VerificationFailedException("Redundant execution of $context detected!") diff --git a/module/exception/src/main/java/org/openbase/jul/exception/ShutdownInProgressException.java b/module/exception/src/main/java/org/openbase/jul/exception/ShutdownInProgressException.java index 1d6af66d0..454a69222 100644 --- a/module/exception/src/main/java/org/openbase/jul/exception/ShutdownInProgressException.java +++ b/module/exception/src/main/java/org/openbase/jul/exception/ShutdownInProgressException.java @@ -41,6 +41,17 @@ public ShutdownInProgressException(final Object service) { super(service + " shutdown in progress!"); } + /** + * {@inheritDoc} + * + * @param message {@inheritDoc} The reason of the shutdown. + * + * Note: The given service should provide a proper toString() method. + */ + public ShutdownInProgressException(final String message) { + super(message); + } + /** * {@inheritDoc} * diff --git a/module/exception/src/main/java/org/openbase/jul/exception/printer/ExceptionPrinter.java b/module/exception/src/main/java/org/openbase/jul/exception/printer/ExceptionPrinter.java index d965a0d2b..04cd6d86e 100644 --- a/module/exception/src/main/java/org/openbase/jul/exception/printer/ExceptionPrinter.java +++ b/module/exception/src/main/java/org/openbase/jul/exception/printer/ExceptionPrinter.java @@ -32,11 +32,8 @@ import org.openbase.jps.exception.JPServiceException; import org.openbase.jps.preset.JPLogLevel; import org.openbase.jps.preset.JPVerbose; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.FatalImplementationErrorException; -import org.openbase.jul.exception.MultiException; +import org.openbase.jul.exception.*; import org.openbase.jul.exception.MultiException.SourceExceptionEntry; -import org.openbase.jul.exception.ShutdownException; import org.slf4j.Logger; /** @@ -182,10 +179,6 @@ public static void printHistory(final T th, final Logger l */ public static void printHistoryAndExit(final T th, final Logger logger) { printHistory(th, logger, LogLevel.ERROR); - if (JPService.testMode()) { - assert false; - return; - } exit(255); } @@ -214,9 +207,6 @@ public static void printHistory(final String message, T th */ public static void printHistoryAndExit(final String message, T th, final Logger logger) { printHistory(new CouldNotPerformException(message, th), logger, LogLevel.ERROR); - if (JPService.testMode()) { - throw new RuntimeException(new ShutdownException(message, th)); - } exit(255); } @@ -274,18 +264,16 @@ public static void printHistory(final String message, fina */ public static void printHistoryAndExit(final String message, final T th, final PrintStream stream) { printHistory(new CouldNotPerformException(message, th), new SystemPrinter(stream)); - if (JPService.testMode()) { - assert false; - return; - } exit(255); } private static void exit(final int errorCode) { - if (JPService.testMode()) { - throw new RuntimeException("System exit called in test mode!"); + // skip in tests since it would interfere with the entire test framework. + if (!JPService.testMode()) { + System.exit(errorCode); } - System.exit(errorCode); + + throw new RuntimeException(new ShutdownInProgressException("Shutdown with error code "+ errorCode + " initiated!")); } /** diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java index a5809e64f..fa8356792 100644 --- a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java @@ -41,6 +41,7 @@ import org.openbase.jul.pattern.Launcher; import org.openbase.jul.pattern.launch.jp.*; import org.openbase.jul.processing.StringProcessor; +import org.openbase.jul.schedule.CloseableWriteLockWrapper; import org.openbase.jul.schedule.FutureProcessor; import org.openbase.jul.schedule.GlobalCachedExecutorService; import org.openbase.jul.schedule.SyncObject; @@ -76,13 +77,12 @@ private static MultiException.ExceptionStack verificationExceptionStack = null; protected final Logger logger = LoggerFactory.getLogger(getClass()); private final Class launchableClass; - private final SyncObject LAUNCHER_LOCK = new SyncObject(this); private final Class applicationClass; private L launchable; private long launchTime = -1; private boolean verified; private VerificationFailedException verificationFailedException; - private Future launcherTask; + volatile private Future launcherTask; /** * Constructor prepares the launcher and registers already a shutdown hook. @@ -326,16 +326,14 @@ public static void main(final Class application, final String[] args, final C } catch (Exception ex) { final CouldNotPerformException exx = new CouldNotPerformException("Could not launch " + launcherEntry.getKey().getSimpleName() + "!", ex); + pushToErrorExceptionStack(application, exx); + // if a core launcher could not be started the whole startup failed so interrupt if (launcherEntry.getValue().isCoreLauncher()) { - pushToErrorExceptionStack(application, ExceptionPrinter.printHistoryAndReturnThrowable(exx, logger)); - // shutdown all launcher forceStopLauncher(launcherMap); } - pushToErrorExceptionStack(application, ExceptionPrinter.printHistoryAndReturnThrowable(exx, logger)); - // finish launcher return null; } @@ -354,17 +352,18 @@ public static void main(final Class application, final String[] args, final C // these exception will be pushed to the error exception stack anyway and printed in the summary } catch (CancellationException ex) { // if a core launcher fails a cancellation exception will be thrown - throw new InterruptedException(); + printSummary(application, logger, generateAppName() + " will be stopped because at least one core laucher could not be started."); + System.exit(200); } } } catch (InterruptedException ex) { - // shutdown all launcher - forceStopLauncher(launcherMap); - // recover interruption Thread.currentThread().interrupt(); + // shutdown all launcher + forceStopLauncher(launcherMap); + // print a summary containing the exceptions printSummary(application, logger, generateAppName() + " caught shutdown signal during startup phase!"); @@ -395,7 +394,7 @@ private static void stopWaiting() { } } - private static void interruptLauncherBoot(final Map, AbstractLauncher> launcherMap) { + private static void interruptLaunch(final Map, AbstractLauncher> launcherMap) { // stop boot stopWaiting(); @@ -408,7 +407,7 @@ private static void interruptLauncherBoot(final Map, AbstractLauncher> launcherMap) { - interruptLauncherBoot(launcherMap); + interruptLaunch(launcherMap); // stop all launcher. This is done in an extra loop since stop can block if the launcher is not yet fully interrupted. for (final Entry, AbstractLauncher> launcherEntryToStop : launcherMap.entrySet()) { @@ -455,7 +454,7 @@ public void init() throws InitializationException, InterruptedException { try { verifyNonRedundantExecution(); } catch (VerificationFailedException e) { - ExceptionPrinter.printHistoryAndExit("Application startup skipped!", e, logger); + throw new InitializationException(this, e); } } @@ -529,7 +528,7 @@ public void verifyNonRedundantExecution() throws VerificationFailedException { try { launcherRemote.activate(); launcherRemote.waitForConnectionState(State.CONNECTED, 1000); - throw new VerificationFailedException("Redundant execution of Launcher[" + getName() + "] detected!"); + throw new RedundantExecutionException("Launcher[" + getName() + "]"); } catch (org.openbase.jul.exception.TimeoutException e) { // this is the default since no other instance should be launched yet. } finally { @@ -547,63 +546,67 @@ public void verifyNonRedundantExecution() throws VerificationFailedException { @Override public Future launch() { - if (launcherTask != null && !launcherTask.isDone()) { - return FutureProcessor.canceledFuture(Void.class, new InvalidStateException("Could not launch " + getName() + "! Application still running!")); - } + try(final CloseableWriteLockWrapper ignored = getManageWriteLockInterruptible(this)) { - launcherTask = GlobalCachedExecutorService.submit(() -> { + if (launcherTask != null && !launcherTask.isDone()) { + return FutureProcessor.canceledFuture(Void.class, new InvalidStateException("Could not launch " + getName() + "! Application still running!")); + } - synchronized (LAUNCHER_LOCK) { + launcherTask = GlobalCachedExecutorService.submit(() -> { - setState(LauncherState.INITIALIZING); + try(final CloseableWriteLockWrapper ignored1 = getManageWriteLockInterruptible(this)) { - try { - init(); - activate(); - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not activate Launcher[" + getName() + "]!", ex, logger); - } catch (RuntimeException e) { - setState(LauncherState.ERROR); - throw e; - } catch (InterruptedException e) { - setState(LauncherState.STOPPED); - throw e; - } + setState(LauncherState.INITIALIZING); - launchable = instantiateLaunchable(); - try { - launchable.init(); - setState(LauncherState.LAUNCHING); + try { + init(); + activate(); + } catch (CouldNotPerformException | RuntimeException e) { + setState(LauncherState.ERROR); + throw e; + } catch (InterruptedException e) { + setState(LauncherState.STOPPED); + throw e; + } - launchable.activate(); - launchTime = System.currentTimeMillis(); - setState(LauncherState.RUNNING); + launchable = instantiateLaunchable(); try { - verify(); - verified = true; - } catch (VerificationFailedException ex) { - verified = false; - verificationFailedException = ex; + launchable.init(); + setState(LauncherState.LAUNCHING); + + launchable.activate(); + launchTime = System.currentTimeMillis(); + setState(LauncherState.RUNNING); + try { + verify(); + verified = true; + } catch (VerificationFailedException ex) { + verified = false; + verificationFailedException = ex; + } + } catch (InterruptedException ex) { + setState(LauncherState.STOPPING); + return null; + } catch (Exception ex) { + setState(LauncherState.ERROR); + launchable.shutdown(); + if (!ExceptionProcessor.isCausedBySystemShutdown(ex)) { + ExceptionPrinter.printHistoryAndReturnThrowable(new CouldNotPerformException("Could not launch " + getName(), ex), logger); + } } - } catch (InterruptedException ex) { - setState(LauncherState.STOPPING); return null; - } catch (Exception ex) { - setState(LauncherState.ERROR); - launchable.shutdown(); - if (!ExceptionProcessor.isCausedBySystemShutdown(ex)) { - ExceptionPrinter.printHistoryAndReturnThrowable(new CouldNotPerformException("Could not launch " + getName(), ex), logger); - } } - return null; - } - }); + }); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new RuntimeException(ex); + } return launcherTask; } @Override public void relaunch() throws CouldNotPerformException, InterruptedException { - synchronized (LAUNCHER_LOCK) { + try(final CloseableWriteLockWrapper ignored = getManageWriteLockInterruptible(this)) { stop(); try { launch().get(); @@ -616,14 +619,16 @@ public void relaunch() throws CouldNotPerformException, InterruptedException { @Override public void stop() { - interruptLaunch(); - - synchronized (LAUNCHER_LOCK) { + try(final CloseableWriteLockWrapper ignored = getManageWriteLockInterruptible(this)) { + interruptLaunch(); setState(LauncherState.STOPPING); if (launchable != null) { launchable.shutdown(); } setState(LauncherState.STOPPED); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new RuntimeException(ex); } } diff --git a/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableLockProvider.java b/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableLockProvider.java index 7cd78657e..81434653f 100644 --- a/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableLockProvider.java +++ b/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableLockProvider.java @@ -1,6 +1,6 @@ package org.openbase.jul.schedule; -/*- +/** * #%L * JUL Schedule * %% @@ -21,7 +21,6 @@ * . * #L% */ - public interface CloseableLockProvider { /** diff --git a/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableReadLockWrapper.java b/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableReadLockWrapper.java index 090434797..07b357c71 100644 --- a/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableReadLockWrapper.java +++ b/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableReadLockWrapper.java @@ -40,7 +40,12 @@ protected CloseableReadLockWrapper(final ReadWriteLock lock, final boolean alloc this.lock = lock; if(allocate) { - this.lock.lockRead(); + try { + this.lock.lockReadInterruptibly(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } } } diff --git a/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableWriteLockWrapper.java b/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableWriteLockWrapper.java index 3a1b5b02e..6c95632fa 100644 --- a/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableWriteLockWrapper.java +++ b/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableWriteLockWrapper.java @@ -38,7 +38,12 @@ public CloseableWriteLockWrapper(final ReadWriteLock lock) { public CloseableWriteLockWrapper(final ReadWriteLock lock, final boolean allocate) { this.lock = lock; if (allocate) { - this.lock.lockWrite(); + try { + this.lock.lockWriteInterruptibly(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } } } From ed92f9ff31269156ca3a7f64fa8da52f7deb03d7 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Wed, 11 Jan 2023 01:30:13 +0100 Subject: [PATCH 09/11] fix javadoc build --- .../java/org/openbase/jul/schedule/CloseableLockProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableLockProvider.java b/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableLockProvider.java index 81434653f..27cf26ebf 100644 --- a/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableLockProvider.java +++ b/module/schedule/src/main/java/org/openbase/jul/schedule/CloseableLockProvider.java @@ -1,6 +1,6 @@ package org.openbase.jul.schedule; -/** +/* * #%L * JUL Schedule * %% From b6677c4ea45cd811a11d3dd26be2dd4d3b340869 Mon Sep 17 00:00:00 2001 From: Tamino Huxohl Date: Thu, 19 Jan 2023 19:49:20 +0100 Subject: [PATCH 10/11] add waitForMiddleware before checking redundant launcher startup --- .../java/org/openbase/jul/pattern/launch/AbstractLauncher.java | 1 + 1 file changed, 1 insertion(+) diff --git a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java index fa8356792..4602588a4 100644 --- a/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java +++ b/module/pattern/launch/src/main/java/org/openbase/jul/pattern/launch/AbstractLauncher.java @@ -527,6 +527,7 @@ public void verifyNonRedundantExecution() throws VerificationFailedException { launcherRemote.init(getScope()); try { launcherRemote.activate(); + launcherRemote.waitForMiddleware(); launcherRemote.waitForConnectionState(State.CONNECTED, 1000); throw new RedundantExecutionException("Launcher[" + getName() + "]"); } catch (org.openbase.jul.exception.TimeoutException e) { From 534e22fd4b7b3eb7a57b1087ce796fe8b2b3347d Mon Sep 17 00:00:00 2001 From: Tamino Huxohl Date: Thu, 19 Jan 2023 20:32:52 +0100 Subject: [PATCH 11/11] AbstractControllerServer now only accesses communicator config once needed --- .../jul/communication/controller/AbstractControllerServer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/module/communication/controller/src/main/java/org/openbase/jul/communication/controller/AbstractControllerServer.java b/module/communication/controller/src/main/java/org/openbase/jul/communication/controller/AbstractControllerServer.java index a1f235da7..45218eb4c 100644 --- a/module/communication/controller/src/main/java/org/openbase/jul/communication/controller/AbstractControllerServer.java +++ b/module/communication/controller/src/main/java/org/openbase/jul/communication/controller/AbstractControllerServer.java @@ -115,7 +115,6 @@ public abstract class AbstractControllerServer