This is an automated email from the ASF dual-hosted git repository.
He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git
The following commit(s) were added to refs/heads/main by this push:
new 7209bc8fd7 Replace scala.Tuple2 with pekko.japi.Pair in
FSMTransitionHandlerBuilder (#3378)
7209bc8fd7 is described below
commit 7209bc8fd7cf749994f1fe38a280bdbdda9a6e9b
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Sat Jul 25 23:26:03 2026 +0800
Replace scala.Tuple2 with pekko.japi.Pair in FSMTransitionHandlerBuilder
(#3378)
* 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
* Add license header to mima filter and migration notes
Motivation:
Address PR review comments: the mima-filters file needs the standard
Apache license header, and the breaking change should be documented in
the migration guide.
Modification:
- Add standard ASF license header to replace-tuple2-with-pair.excludes
- Add FSMTransitionHandlerBuilder breaking change entry to
migration-guide-1.x-2.x.md
Result:
Both review comments addressed.
Tests:
Not run - docs and mima filter only
References:
Refs #2086
---
.../pekko/japi/pf/FSMTransitionHandlerBuilder.java | 38 +++++++++++-----------
.../replace-tuple2-with-pair.excludes | 22 +++++++++++++
.../scala/org/apache/pekko/actor/AbstractFSM.scala | 10 ++++--
.../paradox/migration/migration-guide-1.x-2.x.md | 4 +++
.../pekko/persistence/fsm/PersistentFSMBase.scala | 10 ++++--
5 files changed, 61 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..4950b6672b
--- /dev/null
+++
b/actor/src/main/mima-filters/2.0.x.backwards.excludes/replace-tuple2-with-pair.excludes
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# 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/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
b/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
index 0355511f17..f19565324c 100644
--- a/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
+++ b/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
@@ -33,3 +33,7 @@ were a few mistakes in the Java API where Scala classes
leaked into some of the
An example is in the Scala DSL for Flow and Source, the `watchTermination`
function call no longer needs an empty param
list before a second param list. Instead of `watchTermination(){ ... }`, you
now must use `watchTermination{ ... }`.
([PR2378](https://github.com/apache/pekko/pull/2378))
+
+In the Java API, `FSMTransitionHandlerBuilder.build` and
`FSMTransitionHandlerBuilder.state` now use
+`pekko.japi.Pair` instead of `scala.Tuple2`. Java users need to update their
code to use `Pair` instead of `Tuple2`.
+([PR3378](https://github.com/apache/pekko/pull/3378))
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]