This is an automated email from the ASF dual-hosted git repository.
pjfanning 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 dfdd3eb55c Rework stack walking code to avoid Class.forName (#3354)
dfdd3eb55c is described below
commit dfdd3eb55cae3235911816803f1cef5b4bce56be
Author: PJ Fanning <[email protected]>
AuthorDate: Wed Jul 15 20:49:50 2026 +0100
Rework stack walking code to avoid Class.forName (#3354)
* try to avoid Class.forName
* scala3
* Update TestKitUtils.scala
* Update TestKitUtils.scala
* Update MultiNodeClusterShardingConfig.scala
---
.../sharding/MultiNodeClusterShardingConfig.scala | 29 ++++++++++++++--------
.../org/apache/pekko/testkit/TestKitUtils.scala | 29 +++++++++++-----------
2 files changed, 33 insertions(+), 25 deletions(-)
diff --git
a/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/MultiNodeClusterShardingConfig.scala
b/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/MultiNodeClusterShardingConfig.scala
index 8db34c6839..532e8a6ddd 100644
---
a/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/MultiNodeClusterShardingConfig.scala
+++
b/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/MultiNodeClusterShardingConfig.scala
@@ -13,7 +13,9 @@
package org.apache.pekko.cluster.sharding
+import java.lang.StackWalker.StackFrame
import java.lang.reflect.Modifier
+import java.util.stream.{ Stream => JStream }
import org.apache.pekko
import pekko.cluster.MultiNodeClusterSpec
@@ -24,34 +26,39 @@ import com.typesafe.config.{ Config, ConfigFactory }
object MultiNodeClusterShardingConfig {
+ private val stackWalker: java.util.function.Function[JStream[StackFrame],
Array[Class[?]]] =
+ (frames: JStream[StackFrame]) =>
+ frames.map(_.getDeclaringClass).toArray[Class[?]]((size: Int) => new
Array[Class[?]](size))
+
private[sharding] def testNameFromCallStack(classToStartFrom: Class[?]):
String = {
- def isAbstractClass(className: String): Boolean = {
+ def isAbstractClass(clazz: Class[?]): Boolean = {
try {
- Modifier.isAbstract(Class.forName(className).getModifiers)
+ Modifier.isAbstract(clazz.getModifiers)
} catch {
case _: Throwable => false // yes catch everything, best effort check
}
}
val startFrom = classToStartFrom.getName
- val filteredStack = Thread.currentThread.getStackTrace.iterator
- .map(_.getClassName)
+ val classes =
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
+ .walk(stackWalker)
+ val filteredStack = classes.iterator
// drop until we find the first occurrence of classToStartFrom
- .dropWhile(!_.startsWith(startFrom))
+ .dropWhile(!_.getName.startsWith(startFrom))
// then continue to the next entry after classToStartFrom that makes
sense
.dropWhile {
- case `startFrom` => true
- case str if str.startsWith(startFrom + "$") => true // lambdas inside
startFrom etc
- case str if isAbstractClass(str) => true
- case _ => false
+ case c if c.getName == startFrom => true
+ case c if c.getName.startsWith(startFrom + "$") => true // lambdas
inside startFrom etc
+ case c if isAbstractClass(c) => true
+ case _ => false
}
if (filteredStack.isEmpty)
- throw new IllegalArgumentException(s"Couldn't find
[${classToStartFrom.getName}] in call stack")
+ throw new IllegalArgumentException(s"Couldn't find [$startFrom] in call
stack")
// sanitize for actor system name
- scrubActorSystemName(filteredStack.next())
+ scrubActorSystemName(filteredStack.next().getName)
}
/**
diff --git a/testkit/src/main/scala/org/apache/pekko/testkit/TestKitUtils.scala
b/testkit/src/main/scala/org/apache/pekko/testkit/TestKitUtils.scala
index b0def8b675..c726b90738 100644
--- a/testkit/src/main/scala/org/apache/pekko/testkit/TestKitUtils.scala
+++ b/testkit/src/main/scala/org/apache/pekko/testkit/TestKitUtils.scala
@@ -27,39 +27,40 @@ import org.apache.pekko.annotation.InternalApi
@InternalApi
private[pekko] object TestKitUtils {
- private val stackWalker: java.util.function.Function[JStream[StackFrame],
Array[String]] =
+ private val stackWalker: java.util.function.Function[JStream[StackFrame],
Array[Class[?]]] =
(frames: JStream[StackFrame]) =>
- frames.map(_.getClassName).toArray[String]((size: Int) => new
Array[String](size))
+ frames.map(_.getDeclaringClass).toArray[Class[?]]((size: Int) => new
Array[Class[?]](size))
def testNameFromCallStack(classToStartFrom: Class[?], testKitRegex: Regex):
String = {
- def isAbstractClass(className: String): Boolean = {
+ def isAbstractClass(clazz: Class[?]): Boolean = {
try {
- Modifier.isAbstract(Class.forName(className).getModifiers)
+ Modifier.isAbstract(clazz.getModifiers)
} catch {
case _: Throwable => false // yes catch everything, best effort check
}
}
val startFrom = classToStartFrom.getName
- val classNames = StackWalker.getInstance().walk(stackWalker)
- val filteredStack = classNames.iterator
+ val classes =
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
+ .walk(stackWalker)
+ val filteredStack = classes.iterator
// drop until we find the first occurrence of classToStartFrom
- .dropWhile(!_.startsWith(startFrom))
+ .dropWhile(!_.getName.startsWith(startFrom))
// then continue to the next entry after classToStartFrom that makes
sense
.dropWhile {
- case `startFrom` => true
- case str if str.startsWith(startFrom + "$") => true // lambdas inside
startFrom etc
- case testKitRegex() => true // testkit
internals
- case str if isAbstractClass(str) => true
- case _ => false
+ case c if c.getName == startFrom => true
+ case c if c.getName.startsWith(startFrom + "$") => true // lambdas
inside startFrom etc
+ case c if testKitRegex.matches(c.getName) => true // testkit
internals
+ case c if isAbstractClass(c) => true
+ case _ => false
}
if (filteredStack.isEmpty)
- throw new IllegalArgumentException(s"Couldn't find
[${classToStartFrom.getName}] in call stack")
+ throw new IllegalArgumentException(s"Couldn't find [$startFrom] in call
stack")
// sanitize for actor system name
- scrubActorSystemName(filteredStack.next())
+ scrubActorSystemName(filteredStack.next().getName)
}
/**
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]