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 2408ac0140 fix: support java.math.BigInteger/BigDecimal in
MetricNumericConverter (#3382)
2408ac0140 is described below
commit 2408ac01402b119010ca9b434680bb04305ac27a
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Sun Jul 26 05:28:33 2026 +0800
fix: support java.math.BigInteger/BigDecimal in MetricNumericConverter
(#3382)
* fix: support java.math.BigInteger/BigDecimal in MetricNumericConverter
Motivation:
MetricNumericConverter.convertNumber only handled scala.math.BigInt and
scala.math.BigDecimal but not java.math.BigInteger and java.math.BigDecimal.
Since Metric.value is typed as java.lang.Number, Java users passing
java.math.BigInteger or java.math.BigDecimal would get an
IllegalArgumentException. The NumberInputStream deserializer also explicitly
allows java.math classes through, so deserialized values could also fail.
Modification:
Add pattern match cases for java.math.BigInteger and java.math.BigDecimal in
convertNumber, placed before the Scala BigInt/BigDecimal cases.
Result:
Java users can now use java.math.BigInteger and java.math.BigDecimal as
Metric values without errors.
Tests:
- sbt "cluster-metrics / Test / testOnly
org.apache.pekko.cluster.metrics.MetricNumericConverterSpec"
- sbt "cluster-metrics / Test / testOnly
org.apache.pekko.cluster.metrics.protobuf.MessageSerializerSpec"
- All tests passed.
References:
None - discovered during Java API audit for Scala number type usage
* fix: support any Number subtype in MetricNumericConverter and
MessageSerializer
Motivation:
Metric.value is typed as java.lang.Number, so any Number subtype should
be accepted without throwing. Previously only Int/Long/Double/Float/BigInt/
BigDecimal were handled; other Number subtypes (Short, Byte, AtomicInteger,
AtomicLong, java.math.BigInteger) would throw IllegalArgumentException.
Additionally, BigInt/BigDecimal fell through to Java ObjectOutputStream
serialization in numberToProto, which is inefficient and a security concern.
Modification:
- Add generic `case n: Number => Left(n.longValue)` catch-all in
convertNumber, with java.math.BigDecimal kept explicit (needs doubleValue)
- Add BigInt/BigDecimal/java.math.BigDecimal and generic Number cases in
numberToProto to use efficient protobuf encoding (Long/Double) instead of
Java serialization fallback
- Expand tests to cover Short, Byte, AtomicInteger, AtomicLong, BigDecimal,
java.math.BigInteger, java.math.BigDecimal round-trip serialization
Result:
Any java.lang.Number subtype is now accepted as a Metric value.
Serialization
uses efficient protobuf encoding for all Number types, eliminating Java
ObjectOutputStream usage for known numeric types.
Tests:
- sbt "cluster-metrics / Test / test" — All 59 tests passed
References:
Refs #3385
---
.../org/apache/pekko/cluster/metrics/Metric.scala | 17 ++++++++------
.../metrics/protobuf/MessageSerializer.scala | 9 +++++++-
.../apache/pekko/cluster/metrics/MetricSpec.scala | 26 ++++++++++++++++++++++
.../metrics/protobuf/MessageSerializerSpec.scala | 6 ++++-
4 files changed, 49 insertions(+), 9 deletions(-)
diff --git
a/cluster-metrics/src/main/scala/org/apache/pekko/cluster/metrics/Metric.scala
b/cluster-metrics/src/main/scala/org/apache/pekko/cluster/metrics/Metric.scala
index df7ee5d67e..fe729c7f9b 100644
---
a/cluster-metrics/src/main/scala/org/apache/pekko/cluster/metrics/Metric.scala
+++
b/cluster-metrics/src/main/scala/org/apache/pekko/cluster/metrics/Metric.scala
@@ -271,13 +271,16 @@ private[metrics] trait MetricNumericConverter {
* May involve rounding or truncation.
*/
def convertNumber(from: Any): Either[Long, Double] = from match {
- case n: Int => Left(n)
- case n: Long => Left(n)
- case n: Double => Right(n)
- case n: Float => Right(n)
- case n: BigInt => Left(n.longValue)
- case n: BigDecimal => Right(n.doubleValue)
- case x => throw new IllegalArgumentException(s"Not a number
[$x]")
+ case n: Int => Left(n)
+ case n: Long => Left(n)
+ case n: Double => Right(n)
+ case n: Float => Right(n)
+ case n: BigInt => Left(n.longValue)
+ case n: BigDecimal => Right(n.doubleValue)
+ case n: java.math.BigInteger => Left(n.longValue)
+ case n: java.math.BigDecimal => Right(n.doubleValue)
+ case n: Number => Left(n.longValue)
+ case x => throw new IllegalArgumentException(s"Not a
number [$x]")
}
}
diff --git
a/cluster-metrics/src/main/scala/org/apache/pekko/cluster/metrics/protobuf/MessageSerializer.scala
b/cluster-metrics/src/main/scala/org/apache/pekko/cluster/metrics/protobuf/MessageSerializer.scala
index 706c221e1a..02144eb92b 100644
---
a/cluster-metrics/src/main/scala/org/apache/pekko/cluster/metrics/protobuf/MessageSerializer.scala
+++
b/cluster-metrics/src/main/scala/org/apache/pekko/cluster/metrics/protobuf/MessageSerializer.scala
@@ -193,7 +193,14 @@ class MessageSerializer(val system: ExtendedActorSystem)
extends SerializerWithS
case n: jl.Long =>
Number.newBuilder().setType(NumberType.Long).setValue64(n)
case n: jl.Float =>
Number.newBuilder().setType(NumberType.Float).setValue32(jl.Float.floatToIntBits(n))
case n: jl.Integer =>
Number.newBuilder().setType(NumberType.Integer).setValue32(n)
- case _ =>
+ case n: BigInt =>
Number.newBuilder().setType(NumberType.Long).setValue64(n.longValue)
+ case n: BigDecimal =>
+
Number.newBuilder().setType(NumberType.Double).setValue64(jl.Double.doubleToLongBits(n.doubleValue))
+ case n: java.math.BigInteger =>
Number.newBuilder().setType(NumberType.Long).setValue64(n.longValue)
+ case n: java.math.BigDecimal =>
+
Number.newBuilder().setType(NumberType.Double).setValue64(jl.Double.doubleToLongBits(n.doubleValue))
+ case n: java.lang.Number =>
Number.newBuilder().setType(NumberType.Long).setValue64(n.longValue)
+ case _ =>
val bos = new ByteArrayOutputStream
val out = new ObjectOutputStream(bos)
out.writeObject(number)
diff --git
a/cluster-metrics/src/test/scala/org/apache/pekko/cluster/metrics/MetricSpec.scala
b/cluster-metrics/src/test/scala/org/apache/pekko/cluster/metrics/MetricSpec.scala
index e8cc708979..a5268eb2ce 100644
---
a/cluster-metrics/src/test/scala/org/apache/pekko/cluster/metrics/MetricSpec.scala
+++
b/cluster-metrics/src/test/scala/org/apache/pekko/cluster/metrics/MetricSpec.scala
@@ -39,6 +39,32 @@ class MetricNumericConverterSpec extends AnyWordSpec with
Matchers with MetricNu
convertNumber(0.0).isRight should ===(true)
}
+ "convert java.math.BigInteger and java.math.BigDecimal" in {
+ convertNumber(java.math.BigInteger.valueOf(42)).isLeft should ===(true)
+ convertNumber(java.math.BigInteger.valueOf(42)).left.get should ===(42L)
+ convertNumber(new java.math.BigDecimal("3.14")).isRight should ===(true)
+ convertNumber(new java.math.BigDecimal("3.14")).right.get should
===(3.14 +- 0.0001)
+ }
+
+ "convert any Number subtype" in {
+ convertNumber(42.toShort).isLeft should ===(true)
+ convertNumber(42.toShort).left.get should ===(42L)
+ convertNumber(7.toByte).isLeft should ===(true)
+ convertNumber(7.toByte).left.get should ===(7L)
+ convertNumber(new java.util.concurrent.atomic.AtomicInteger(99)).isLeft
should ===(true)
+ convertNumber(new
java.util.concurrent.atomic.AtomicInteger(99)).left.get should ===(99L)
+ convertNumber(new java.util.concurrent.atomic.AtomicLong(1234L)).isLeft
should ===(true)
+ convertNumber(new
java.util.concurrent.atomic.AtomicLong(1234L)).left.get should ===(1234L)
+ }
+
+ "define a metric with java.math.BigInteger and java.math.BigDecimal
values" in {
+ val Some(intMetric) = Metric.create("big-int",
java.math.BigInteger.valueOf(1024L), None): @unchecked
+ intMetric.value should ===(java.math.BigInteger.valueOf(1024L))
+
+ val Some(decMetric) = Metric.create("big-dec", new
java.math.BigDecimal("99.9"), None): @unchecked
+ decMetric.value should ===(new java.math.BigDecimal("99.9"))
+ }
+
"define a new metric" in {
val Some(metric) = Metric.create(HeapMemoryUsed, 256L, decayFactor =
Some(0.18)): @unchecked
metric.name should ===(HeapMemoryUsed)
diff --git
a/cluster-metrics/src/test/scala/org/apache/pekko/cluster/metrics/protobuf/MessageSerializerSpec.scala
b/cluster-metrics/src/test/scala/org/apache/pekko/cluster/metrics/protobuf/MessageSerializerSpec.scala
index 3a538c4a77..21b16ccd06 100644
---
a/cluster-metrics/src/test/scala/org/apache/pekko/cluster/metrics/protobuf/MessageSerializerSpec.scala
+++
b/cluster-metrics/src/test/scala/org/apache/pekko/cluster/metrics/protobuf/MessageSerializerSpec.scala
@@ -63,7 +63,11 @@ class MessageSerializerSpec extends PekkoSpec("""
Metric("bar2", Float.MaxValue, None),
Metric("bar3", Int.MaxValue, None),
Metric("bar4", Long.MaxValue, None),
- Metric("bar5", BigInt(Long.MaxValue), None)))))
+ Metric("bar5", BigInt(Long.MaxValue), None),
+ Metric("bar6", BigDecimal("3.14"), None),
+ Metric("bar7", java.math.BigInteger.valueOf(9999L), None),
+ Metric("bar8", new java.math.BigDecimal("2.718"), None),
+ Metric("bar9", 42.toShort, None)))))
checkSerialization(MetricsGossipEnvelope(a1.address, metricsGossip,
true))
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]