diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml
index d58053c..7d494f9 100644
--- a/.github/workflows/maven-publish.yml
+++ b/.github/workflows/maven-publish.yml
@@ -1,7 +1,7 @@
name: Maven Publish
on:
release:
- types: [created]
+ types: [ created ]
jobs:
publish:
runs-on: ubuntu-latest
@@ -15,8 +15,14 @@ jobs:
server-id: ossrh
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
+
+ - name: Install GPG secret key
+ run: |
+ cat <(echo -e "${{ secrets.OSSRH_GPG_SECRET_KEY }}") | gpg --batch --import
+ gpg --list-secret-keys --keyid-format LONG
+
- name: Publish package
- run: mvn --batch-mode deploy
+ run: mvn --batch-mode -Dgpg.passphrase=${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} clean deploy -P release
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
diff --git a/pom.xml b/pom.xml
index a9bb1b3..4ed88f6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.digitalpetri.fsm
strict-machine
- 1.0.0-SNAPSHOT
+ 1.0.0
Strict Machine
@@ -44,6 +44,9 @@
11
UTF-8
+
+ 2.0.16
+
5.10.2
@@ -54,6 +57,12 @@
+
+ org.slf4j
+ slf4j-api
+ 2.0.16
+
+
org.junit.jupiter
junit-jupiter-api
diff --git a/src/main/java/com/digitalpetri/fsm/FsmContext.java b/src/main/java/com/digitalpetri/fsm/FsmContext.java
index 270a4fa..48b8827 100644
--- a/src/main/java/com/digitalpetri/fsm/FsmContext.java
+++ b/src/main/java/com/digitalpetri/fsm/FsmContext.java
@@ -76,7 +76,7 @@ public interface FsmContext {
*
* @return the user-configurable context associated with this FSM instance.
*/
- Object getContext();
+ Object getUserContext();
final class Key {
diff --git a/src/main/java/com/digitalpetri/fsm/FsmLogging.java b/src/main/java/com/digitalpetri/fsm/FsmLogging.java
deleted file mode 100644
index 59546f6..0000000
--- a/src/main/java/com/digitalpetri/fsm/FsmLogging.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2024 Kevin Herron
- *
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-
-package com.digitalpetri.fsm;
-
-public final class FsmLogging {
-
- private FsmLogging() {}
-
- /**
- * Configure the {@link Callback} to use for logging.
- *
- * @param callback the {@link Callback} to use for logging.
- */
- public static void configure(Callback callback) {
- Log.CALLBACK.set(callback);
- }
-
- public interface Callback {
-
- /**
- * Log a message at the given {@link Level}.
- *
- * @param context the user-configurable context. May be {@code null} even when configured if
- * the message originates from a global context.
- * @param level the {@link Level} to log at.
- * @param message the message.
- */
- void log(Object context, Level level, String message);
-
- /**
- * Check if logging is enabled for the given {@link Level}.
- *
- * @param level the {@link Level} to check.
- * @return {@code true} if logging is enabled for the given {@link Level}.
- */
- default boolean isEnabled(Level level) {
- return true;
- }
-
- }
-
- public enum Level {
- TRACE,
- DEBUG,
- INFO,
- WARN,
- ERROR
- }
-
- /**
- * A logging {@link Callback} that logs to {@link System#out}.
- */
- public static final Callback SYSTEM_OUT_CALLBACK =
- (context, level, message) -> System.out.printf("%s: %s%n", level, message);
-
-}
diff --git a/src/main/java/com/digitalpetri/fsm/Log.java b/src/main/java/com/digitalpetri/fsm/Log.java
deleted file mode 100644
index 1a22798..0000000
--- a/src/main/java/com/digitalpetri/fsm/Log.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (c) 2024 Kevin Herron
- *
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-
-package com.digitalpetri.fsm;
-
-import com.digitalpetri.fsm.FsmLogging.Level;
-import java.util.concurrent.atomic.AtomicReference;
-
-public class Log {
-
- public static final AtomicReference CALLBACK = new AtomicReference<>();
-
- private Log() {}
-
- /**
- * Log a message at {@link Level#TRACE}.
- *
- * @param context the user-configurable context. May be {@code null} even when configured.
- * @param format the message format.
- * @param args the message arguments.
- */
- public static void trace(Object context, String format, Object... args) {
- log(context, Level.TRACE, format, args);
- }
-
- /**
- * Log a message at {@link Level#DEBUG}.
- *
- * @param context the user-configurable context. May be {@code null} even when configured.
- * @param format the message format.
- * @param args the message arguments.
- */
- public static void debug(Object context, String format, Object... args) {
- log(context, Level.DEBUG, format, args);
- }
-
- /**
- * Log a message at {@link Level#INFO}.
- *
- * @param context the user-configurable context. May be {@code null} even when configured.
- * @param format the message format.
- * @param args the message arguments.
- */
- public static void info(Object context, String format, Object... args) {
- log(context, Level.INFO, format, args);
- }
-
- /**
- * Log a message at {@link Level#WARN}.
- *
- * @param context the user-configurable context. May be {@code null} even when configured.
- * @param format the message format.
- * @param args the message arguments.
- */
- public static void warn(Object context, String format, Object... args) {
- log(context, Level.WARN, format, args);
- }
-
- /**
- * Log a message at {@link Level#ERROR}.
- *
- * @param context the user-configurable context. May be {@code null} even when configured.
- * @param format the message format.
- * @param args the message arguments.
- */
- public static void error(Object context, String format, Object... args) {
- log(context, Level.ERROR, format, args);
- }
-
- /**
- * Check if logging is enabled for the given {@link Level}.
- *
- * @param level the {@link Level} to check.
- * @return {@code true} if logging is enabled for the given {@link Level}.
- */
- public static boolean isLevelEnabled(FsmLogging.Level level) {
- FsmLogging.Callback callback = CALLBACK.get();
-
- return callback == null || callback.isEnabled(level);
- }
-
- /**
- * Log a message at the given {@link Level}.
- *
- * @param context the user-configurable context. May be {@code null} even when configured.
- * @param level the {@link Level} to log at.
- * @param format the message format.
- * @param args the message arguments.
- */
- public static void log(Object context, Level level, String format, Object... args) {
- FsmLogging.Callback callback = CALLBACK.get();
- if (callback != null && callback.isEnabled(level)) {
- callback.log(context, level, String.format(format, args));
- }
- }
-
-}
diff --git a/src/main/java/com/digitalpetri/fsm/StrictMachine.java b/src/main/java/com/digitalpetri/fsm/StrictMachine.java
index e4c6b41..643ad83 100644
--- a/src/main/java/com/digitalpetri/fsm/StrictMachine.java
+++ b/src/main/java/com/digitalpetri/fsm/StrictMachine.java
@@ -10,7 +10,6 @@
package com.digitalpetri.fsm;
-import com.digitalpetri.fsm.FsmLogging.Level;
import com.digitalpetri.fsm.dsl.ActionContext;
import com.digitalpetri.fsm.dsl.ActionProxy;
import com.digitalpetri.fsm.dsl.Transition;
@@ -26,6 +25,9 @@
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.function.Function;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
public class StrictMachine implements Fsm {
@@ -38,23 +40,29 @@ public class StrictMachine implements Fsm {
private final Map, Object> contextValues = new ConcurrentHashMap<>();
private final AtomicReference state = new AtomicReference<>();
- private final Object context;
+ private final Logger logger;
+ private final Map mdc;
private final Executor executor;
+ private final Object userContext;
private final ActionProxy actionProxy;
private final List> transitions;
private final List> transitionActions;
public StrictMachine(
- Object context,
+ String loggerName,
+ Map mdc,
Executor executor,
+ Object userContext,
ActionProxy actionProxy,
S initialState,
List> transitions,
List> transitionActions
) {
- this.context = context;
+ this.logger = LoggerFactory.getLogger(loggerName);
+ this.mdc = mdc;
this.executor = executor;
+ this.userContext = userContext;
this.actionProxy = actionProxy;
this.transitions = transitions;
this.transitionActions = transitionActions;
@@ -171,14 +179,18 @@ public void run() {
state.set(nextState);
- if (Log.isLevelEnabled(Level.DEBUG)) {
- Log.debug(
- context,
- "%s x %s = %s",
- padRight(String.format("S(%s)", currState)),
- padRight(String.format("E(%s)", event)),
- padRight(String.format("S'(%s)", nextState))
- );
+ if (logger.isDebugEnabled()) {
+ mdc.forEach(MDC::put);
+ try {
+ logger.debug(
+ "{} x {} = {}",
+ padRight(String.format("S(%s)", currState)),
+ padRight(String.format("E(%s)", event)),
+ padRight(String.format("S'(%s)", nextState))
+ );
+ } finally {
+ mdc.keySet().forEach(MDC::remove);
+ }
}
var actionContext = new ActionContextImpl(
@@ -195,27 +207,49 @@ public void run() {
}
}
- Log.trace(context, "found %d matching TransitionActions", matchingActions.size());
+ if (logger.isTraceEnabled()) {
+ mdc.forEach(MDC::put);
+ try {
+ logger.trace("found {} matching TransitionActions", matchingActions.size());
+ } finally {
+ mdc.keySet().forEach(MDC::remove);
+ }
+ }
matchingActions.forEach(transitionAction -> {
try {
if (actionProxy == null) {
- Log.trace(context, "executing TransitionAction: %s", transitionAction);
+ if (logger.isTraceEnabled()) {
+ mdc.forEach(MDC::put);
+ try {
+ logger.trace("executing TransitionAction: {}", transitionAction);
+ } finally {
+ mdc.keySet().forEach(MDC::remove);
+ }
+ }
transitionAction.execute(actionContext);
} else {
- Log.trace(
- context,
- "executing (via proxy) TransitionAction: %s", transitionAction
- );
+ if (logger.isTraceEnabled()) {
+ mdc.forEach(MDC::put);
+ try {
+ logger.trace("executing (via proxy) TransitionAction: {}", transitionAction);
+ } finally {
+ mdc.keySet().forEach(MDC::remove);
+ }
+ }
actionProxy.execute(actionContext, transitionAction::execute);
}
} catch (Throwable ex) {
- Log.warn(
- context,
- "Uncaught Throwable executing TransitionAction: %s\n%s", transitionAction, ex
- );
+
+ mdc.forEach(MDC::put);
+ try {
+ logger.warn("uncaught Throwable executing TransitionAction: {}",
+ transitionAction, ex);
+ } finally {
+ mdc.keySet().forEach(MDC::remove);
+ }
}
});
@@ -318,8 +352,8 @@ public void set(Key> key, Object value) {
}
@Override
- public Object getContext() {
- return context;
+ public Object getUserContext() {
+ return userContext;
}
}
diff --git a/src/main/java/com/digitalpetri/fsm/dsl/FsmBuilder.java b/src/main/java/com/digitalpetri/fsm/dsl/FsmBuilder.java
index e39ae79..c794524 100644
--- a/src/main/java/com/digitalpetri/fsm/dsl/FsmBuilder.java
+++ b/src/main/java/com/digitalpetri/fsm/dsl/FsmBuilder.java
@@ -15,6 +15,7 @@
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
@@ -34,18 +35,34 @@ public class FsmBuilder, E> {
private ActionProxy actionProxy = null;
- private final Object context;
+ private final String loggerName;
+ private final Map mdc;
private final Executor executor;
+ private final Object userContext;
public FsmBuilder() {
- this(INSTANCE_ID.getAndIncrement(), EXECUTOR_SERVICE);
+ this(
+ StrictMachine.class.getName(),
+ Map.of(),
+ EXECUTOR_SERVICE,
+ null
+ );
}
- public FsmBuilder(Object context, Executor executor) {
- this.context = context != null ? context : INSTANCE_ID.getAndIncrement();
+ public FsmBuilder(
+ String loggerName,
+ Map mdc,
+ Executor executor,
+ Object userContext
+ ) {
+
+ this.loggerName = loggerName;
+ this.mdc = mdc;
this.executor = executor;
+ this.userContext = userContext;
}
+
/**
* Start defining a {@link Transition} from state {@code state}.
*
@@ -145,8 +162,10 @@ public void setActionProxy(ActionProxy actionProxy) {
public Fsm build(S initialState) {
return new StrictMachine<>(
- context,
+ loggerName,
+ mdc,
executor,
+ userContext,
actionProxy,
initialState,
new ArrayList<>(transitions),