This is an automated email from the ASF dual-hosted git repository. He-Pin pushed a commit to branch java-api-replace-tuple2-with-pair in repository https://gitbox.apache.org/repos/asf/pekko.git
commit 91f7b8454f7e4e0c2f94868ba4de6292f392771f Author: 虎鸣 <[email protected]> AuthorDate: Sat Jul 25 19:29:40 2026 +0800 Replace scala.Tuple2 with pekko.japi.Pair in FSMTransitionHandlerBuilder Motivation: Java users see scala.Tuple2 in their IDE when using FSMTransitionHandlerBuilder, which violates the principle of avoiding Scala data structures in Java APIs. Modification: - Replace scala.Tuple2 with pekko.japi.Pair in FSMTransitionHandlerBuilder - Update AbstractFSM and PersistentFSMBase to use a proper adapter instead of an unsafe .asInstanceOf[TransitionHandler] cast - Add MiMa filters for the binary incompatibility Result: Java FSM API no longer exposes scala.Tuple2; uses the established pekko.japi.Pair type instead. The adapter is also type-safe (no more unchecked cast). Tests: - sbt "actor-tests / Test / testOnly org.apache.pekko.actor.AbstractFSMActorTest" PASSED - sbt "docs / Test / testOnly jdocs.actor.fsm.*" PASSED - sbt "persistence / Test / testOnly org.apache.pekko.persistence.fsm.InmemPersistentFSMSpec" PASSED - sbt "actor / mimaReportBinaryIssues" PASSED References: Refs #2086 --- .../pekko/japi/pf/FSMTransitionHandlerBuilder.java | 38 +++++++++++----------- .../replace-tuple2-with-pair.excludes | 5 +++ .../scala/org/apache/pekko/actor/AbstractFSM.scala | 10 ++++-- .../pekko/persistence/fsm/PersistentFSMBase.scala | 10 ++++-- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/actor/src/main/java/org/apache/pekko/japi/pf/FSMTransitionHandlerBuilder.java b/actor/src/main/java/org/apache/pekko/japi/pf/FSMTransitionHandlerBuilder.java index 4447adfe79..c0793df780 100644 --- a/actor/src/main/java/org/apache/pekko/japi/pf/FSMTransitionHandlerBuilder.java +++ b/actor/src/main/java/org/apache/pekko/japi/pf/FSMTransitionHandlerBuilder.java @@ -13,12 +13,12 @@ package org.apache.pekko.japi.pf; +import org.apache.pekko.japi.Pair; import org.apache.pekko.japi.function.Effect; import org.apache.pekko.japi.function.Predicate; import org.apache.pekko.japi.function.Procedure; import org.apache.pekko.japi.function.Procedure2; import scala.PartialFunction; -import scala.Tuple2; import scala.runtime.BoxedUnit; /** @@ -28,7 +28,7 @@ import scala.runtime.BoxedUnit; */ public class FSMTransitionHandlerBuilder<S> { - private final UnitPFBuilder<Tuple2<S, S>> builder = new UnitPFBuilder<Tuple2<S, S>>(); + private final UnitPFBuilder<Pair<S, S>> builder = new UnitPFBuilder<Pair<S, S>>(); /** * Add a case statement that matches on a from state and a to state. @@ -41,17 +41,17 @@ public class FSMTransitionHandlerBuilder<S> { public FSMTransitionHandlerBuilder<S> state( final S fromState, final S toState, final Effect apply) { builder.match( - Tuple2.class, - new Predicate<Tuple2>() { + Pair.class, + new Predicate<Pair>() { @Override - public boolean test(Tuple2 t) { - return (fromState == null || fromState.equals(t._1())) - && (toState == null || toState.equals(t._2())); + public boolean test(Pair t) { + return (fromState == null || fromState.equals(t.first())) + && (toState == null || toState.equals(t.second())); } }, - new Procedure<Tuple2>() { + new Procedure<Pair>() { @Override - public void apply(Tuple2 t) throws Exception { + public void apply(Pair t) throws Exception { apply.apply(); } }); @@ -69,21 +69,21 @@ public class FSMTransitionHandlerBuilder<S> { public FSMTransitionHandlerBuilder<S> state( final S fromState, final S toState, final Procedure2<S, S> apply) { builder.match( - Tuple2.class, - new Predicate<Tuple2>() { + Pair.class, + new Predicate<Pair>() { @Override - public boolean test(Tuple2 t) { - return (fromState == null || fromState.equals(t._1())) - && (toState == null || toState.equals(t._2())); + public boolean test(Pair t) { + return (fromState == null || fromState.equals(t.first())) + && (toState == null || toState.equals(t.second())); } }, - new Procedure<Tuple2>() { + new Procedure<Pair>() { @Override - public void apply(Tuple2 t) throws Exception { + public void apply(Pair t) throws Exception { @SuppressWarnings("unchecked") - S sf = (S) t._1(); + S sf = (S) t.first(); @SuppressWarnings("unchecked") - S st = (S) t._2(); + S st = (S) t.second(); apply.apply(sf, st); } }); @@ -96,7 +96,7 @@ public class FSMTransitionHandlerBuilder<S> { * * @return a PartialFunction for this builder. */ - public PartialFunction<Tuple2<S, S>, BoxedUnit> build() { + public PartialFunction<Pair<S, S>, BoxedUnit> build() { return builder.build(); } } diff --git a/actor/src/main/mima-filters/2.0.x.backwards.excludes/replace-tuple2-with-pair.excludes b/actor/src/main/mima-filters/2.0.x.backwards.excludes/replace-tuple2-with-pair.excludes new file mode 100644 index 0000000000..5ea8aa4423 --- /dev/null +++ b/actor/src/main/mima-filters/2.0.x.backwards.excludes/replace-tuple2-with-pair.excludes @@ -0,0 +1,5 @@ +# Replace scala.Tuple2 with pekko.japi.Pair in FSMTransitionHandlerBuilder +# https://github.com/apache/pekko/issues/2086 +ProblemFilters.exclude[IncompatibleResultTypeProblem]("org.apache.pekko.japi.pf.FSMTransitionHandlerBuilder.build") +ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.japi.pf.FSMTransitionHandlerBuilder.build") +ProblemFilters.exclude[IncompatibleMethTypeProblem]("org.apache.pekko.japi.pf.FSMTransitionHandlerBuilder.state") diff --git a/actor/src/main/scala/org/apache/pekko/actor/AbstractFSM.scala b/actor/src/main/scala/org/apache/pekko/actor/AbstractFSM.scala index fd26155ced..e8e33c15e8 100644 --- a/actor/src/main/scala/org/apache/pekko/actor/AbstractFSM.scala +++ b/actor/src/main/scala/org/apache/pekko/actor/AbstractFSM.scala @@ -17,6 +17,7 @@ import scala.concurrent.duration.FiniteDuration import scala.jdk.DurationConverters._ import org.apache.pekko +import pekko.japi.Pair import pekko.japi.function.{ Effect, Function2, Predicate, Predicate2, Procedure, Procedure2, Procedure3 } /** @@ -169,8 +170,13 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] { * <b>Multiple handlers may be installed, and every one of them will be * called, not only the first one matching.</b> */ - final def onTransition(transitionHandlerBuilder: FSMTransitionHandlerBuilder[S]): Unit = - super.onTransition(transitionHandlerBuilder.build().asInstanceOf[TransitionHandler]) + final def onTransition(transitionHandlerBuilder: FSMTransitionHandlerBuilder[S]): Unit = { + val pf = transitionHandlerBuilder.build() + super.onTransition(new TransitionHandler { + def isDefinedAt(in: (S, S)): Boolean = pf.isDefinedAt(Pair(in._1, in._2)) + def apply(in: (S, S)): Unit = pf(Pair(in._1, in._2)) + }) + } /** * Add a handler which is called upon each state transition, i.e. not when diff --git a/persistence/src/main/scala/org/apache/pekko/persistence/fsm/PersistentFSMBase.scala b/persistence/src/main/scala/org/apache/pekko/persistence/fsm/PersistentFSMBase.scala index 1e7455a0fb..4a47db624a 100644 --- a/persistence/src/main/scala/org/apache/pekko/persistence/fsm/PersistentFSMBase.scala +++ b/persistence/src/main/scala/org/apache/pekko/persistence/fsm/PersistentFSMBase.scala @@ -23,6 +23,7 @@ import language.implicitConversions import org.apache.pekko import pekko.actor._ import pekko.japi.function.{ Effect, Function2, Predicate, Predicate2, Procedure, Procedure2, Procedure3 } +import pekko.japi.Pair import pekko.japi.pf.{ FSMTransitionHandlerBuilder, UnitMatch, UnitPFBuilder } import pekko.routing.{ Deafen, Listen, Listeners } @@ -850,8 +851,13 @@ abstract class AbstractPersistentFSMBase[S, D, E] extends PersistentFSMBase[S, D * <b>Multiple handlers may be installed, and every one of them will be * called, not only the first one matching.</b> */ - final def onTransition(transitionHandlerBuilder: FSMTransitionHandlerBuilder[S]): Unit = - onTransition(transitionHandlerBuilder.build().asInstanceOf[TransitionHandler]) + final def onTransition(transitionHandlerBuilder: FSMTransitionHandlerBuilder[S]): Unit = { + val pf = transitionHandlerBuilder.build() + onTransition(new TransitionHandler { + def isDefinedAt(in: (S, S)): Boolean = pf.isDefinedAt(Pair(in._1, in._2)) + def apply(in: (S, S)): Unit = pf(Pair(in._1, in._2)) + }) + } /** * Add a handler which is called upon each state transition, i.e. not when --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
