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]

Reply via email to