From 5df247a3e380e76ca71d1c475d8d413ceb42a882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=8E=E9=B8=A3?= Date: Mon, 6 Jul 2026 14:36:18 +0800 Subject: [PATCH] refactor: replace deprecated `extends App` with explicit main method Motivation: `extends App` is deprecated in Scala 3. Replace with explicit `def main` method for forward compatibility. Modification: Replace `object X extends App { ... }` with `object X { def main(args: Array[String]): Unit = { ... } }` in 5 sample application files. Result: No more usage of deprecated `scala.App` trait. Code is compatible with Scala 3. Tests: Not run - docs only References: None - Scala 3 compatibility --- .../scala/com/example/ClusteringApp.scala | 10 ++- .../sample/cluster/kubernetes/DemoApp.scala | 38 ++++----- .../src/main/scala/client/ClientApp.scala | 36 +++++---- .../sharding/embeddedkafka/KafkaBroker.scala | 24 +++--- .../kafka/producer/UserEventProducer.scala | 78 ++++++++++--------- 5 files changed, 98 insertions(+), 88 deletions(-) diff --git a/pekko-sample-cluster-docker-compose-scala/src/main/scala/com/example/ClusteringApp.scala b/pekko-sample-cluster-docker-compose-scala/src/main/scala/com/example/ClusteringApp.scala index 915d3822..ab916851 100644 --- a/pekko-sample-cluster-docker-compose-scala/src/main/scala/com/example/ClusteringApp.scala +++ b/pekko-sample-cluster-docker-compose-scala/src/main/scala/com/example/ClusteringApp.scala @@ -3,9 +3,11 @@ package com.example import org.apache.pekko.actor.typed.ActorSystem import com.typesafe.config.ConfigFactory -object ClusteringApp extends App { - val config = ConfigFactory.load() - val clusterName = config.getString("clustering.cluster.name") +object ClusteringApp { + def main(args: Array[String]): Unit = { + val config = ConfigFactory.load() + val clusterName = config.getString("clustering.cluster.name") - ActorSystem(ClusterListener(), clusterName) + ActorSystem(ClusterListener(), clusterName) + } } diff --git a/pekko-sample-cluster-kubernetes-scala/src/main/scala/pekko/sample/cluster/kubernetes/DemoApp.scala b/pekko-sample-cluster-kubernetes-scala/src/main/scala/pekko/sample/cluster/kubernetes/DemoApp.scala index 928209c4..bae234d1 100644 --- a/pekko-sample-cluster-kubernetes-scala/src/main/scala/pekko/sample/cluster/kubernetes/DemoApp.scala +++ b/pekko-sample-cluster-kubernetes-scala/src/main/scala/pekko/sample/cluster/kubernetes/DemoApp.scala @@ -12,28 +12,30 @@ import org.apache.pekko.{ actor => classic } import scala.concurrent.ExecutionContext -object DemoApp extends App { +object DemoApp { + def main(args: Array[String]): Unit = { - ActorSystem[Nothing](Behaviors.setup[Nothing] { context => - import org.apache.pekko.actor.typed.scaladsl.adapter._ - implicit val classicSystem: classic.ActorSystem = context.system.toClassic - implicit val ec: ExecutionContext = context.system.executionContext + ActorSystem[Nothing](Behaviors.setup[Nothing] { context => + import org.apache.pekko.actor.typed.scaladsl.adapter._ + implicit val classicSystem: classic.ActorSystem = context.system.toClassic + implicit val ec: ExecutionContext = context.system.executionContext - val cluster = Cluster(context.system) - context.log.info("Started [" + context.system + "], cluster.selfAddress = " + cluster.selfMember.address + ")") + val cluster = Cluster(context.system) + context.log.info("Started [" + context.system + "], cluster.selfAddress = " + cluster.selfMember.address + ")") - Http().newServerAt("0.0.0.0", 8080).bind(complete("Hello world")) + Http().newServerAt("0.0.0.0", 8080).bind(complete("Hello world")) - // Create an actor that handles cluster domain events - val listener = context.spawn(Behaviors.receive[ClusterEvent.MemberEvent]((ctx, event) => { - ctx.log.info("MemberEvent: {}", event) - Behaviors.same - }), "listener") + // Create an actor that handles cluster domain events + val listener = context.spawn(Behaviors.receive[ClusterEvent.MemberEvent]((ctx, event) => { + ctx.log.info("MemberEvent: {}", event) + Behaviors.same + }), "listener") - Cluster(context.system).subscriptions ! Subscribe(listener, classOf[ClusterEvent.MemberEvent]) + Cluster(context.system).subscriptions ! Subscribe(listener, classOf[ClusterEvent.MemberEvent]) - PekkoManagement.get(classicSystem).start() - ClusterBootstrap.get(classicSystem).start() - Behaviors.empty - }, "appka") + PekkoManagement.get(classicSystem).start() + ClusterBootstrap.get(classicSystem).start() + Behaviors.empty + }, "appka") + } } diff --git a/pekko-sample-kafka-to-sharding-scala/client/src/main/scala/client/ClientApp.scala b/pekko-sample-kafka-to-sharding-scala/client/src/main/scala/client/ClientApp.scala index 80495067..2b99b264 100644 --- a/pekko-sample-kafka-to-sharding-scala/client/src/main/scala/client/ClientApp.scala +++ b/pekko-sample-kafka-to-sharding-scala/client/src/main/scala/client/ClientApp.scala @@ -11,24 +11,26 @@ import scala.concurrent.ExecutionContextExecutor import scala.concurrent.duration.Duration import scala.io.StdIn -object ClientApp extends App { - implicit val system: ActorSystem = ActorSystem("UserClient") - implicit val mat: Materializer = Materializer.createMaterializer(system) - implicit val ec: ExecutionContextExecutor = system.dispatcher - val clientSettings = GrpcClientSettings.connectToServiceAt("127.0.0.1", 8081).withTls(false) - val client = UserServiceClient(clientSettings) +object ClientApp { + def main(args: Array[String]): Unit = { + implicit val system: ActorSystem = ActorSystem("UserClient") + implicit val mat: Materializer = Materializer.createMaterializer(system) + implicit val ec: ExecutionContextExecutor = system.dispatcher + val clientSettings = GrpcClientSettings.connectToServiceAt("127.0.0.1", 8081).withTls(false) + val client = UserServiceClient(clientSettings) - var userId = "" - while (userId != ":q") { - println("Enter user id or :q to quit") - userId = StdIn.readLine() - if (userId != ":q") { - val runningTotal = Await.result(client.userStats(UserStatsRequest(userId)), Duration.Inf) - println( - s"User ${userId} has made ${runningTotal.totalPurchases} purchases for a total of ${runningTotal.amountSpent}p") - } + var userId = "" + while (userId != ":q") { + println("Enter user id or :q to quit") + userId = StdIn.readLine() + if (userId != ":q") { + val runningTotal = Await.result(client.userStats(UserStatsRequest(userId)), Duration.Inf) + println( + s"User ${userId} has made ${runningTotal.totalPurchases} purchases for a total of ${runningTotal.amountSpent}p") + } + } + println("Exiting") + system.terminate() } - println("Exiting") - system.terminate() } diff --git a/pekko-sample-kafka-to-sharding-scala/kafka/src/main/scala/sample/sharding/embeddedkafka/KafkaBroker.scala b/pekko-sample-kafka-to-sharding-scala/kafka/src/main/scala/sample/sharding/embeddedkafka/KafkaBroker.scala index f1eb3b9d..0753d19d 100644 --- a/pekko-sample-kafka-to-sharding-scala/kafka/src/main/scala/sample/sharding/embeddedkafka/KafkaBroker.scala +++ b/pekko-sample-kafka-to-sharding-scala/kafka/src/main/scala/sample/sharding/embeddedkafka/KafkaBroker.scala @@ -3,20 +3,22 @@ package sample.sharding.embeddedkafka import net.manub.embeddedkafka.{ EmbeddedKafka, EmbeddedKafkaConfig } import org.slf4j.LoggerFactory -object KafkaBroker extends App with EmbeddedKafka { - val log = LoggerFactory.getLogger(this.getClass) +object KafkaBroker extends EmbeddedKafka { + def main(args: Array[String]): Unit = { + val log = LoggerFactory.getLogger(this.getClass) - val port = 9092 - val topic = "user-events" - val partitions = 128 + val port = 9092 + val topic = "user-events" + val partitions = 128 - implicit val config: EmbeddedKafkaConfig = EmbeddedKafkaConfig(kafkaPort = port) - val server = EmbeddedKafka.start() + implicit val config: EmbeddedKafkaConfig = EmbeddedKafkaConfig(kafkaPort = port) + val server = EmbeddedKafka.start() - createCustomTopic(topic = topic, partitions = partitions) + createCustomTopic(topic = topic, partitions = partitions) - log.info(s"Kafka running: localhost:$port") - log.info(s"Topic '$topic' with $partitions partitions created") + log.info(s"Kafka running: localhost:$port") + log.info(s"Topic '$topic' with $partitions partitions created") - server.broker.awaitShutdown() + server.broker.awaitShutdown() + } } diff --git a/pekko-sample-kafka-to-sharding-scala/producer/src/main/scala/sharding/kafka/producer/UserEventProducer.scala b/pekko-sample-kafka-to-sharding-scala/producer/src/main/scala/sharding/kafka/producer/UserEventProducer.scala index 74621c41..f8a32a45 100644 --- a/pekko-sample-kafka-to-sharding-scala/producer/src/main/scala/sharding/kafka/producer/UserEventProducer.scala +++ b/pekko-sample-kafka-to-sharding-scala/producer/src/main/scala/sharding/kafka/producer/UserEventProducer.scala @@ -15,42 +15,44 @@ import scala.concurrent.Future import scala.concurrent.duration._ import scala.util.Random -object UserEventProducer extends App { - - implicit val system: ActorSystem = ActorSystem( - "UserEventProducer", - ConfigFactory.parseString(""" - pekko.actor.provider = "local" - """.stripMargin).withFallback(ConfigFactory.load()).resolve()) - - val log = Logging(system, "UserEventProducer") - - val config = system.settings.config.getConfig("pekko.kafka.producer") - - val producerConfig = ProducerConfig(system.settings.config.getConfig("kafka-to-sharding-producer")) - - val producerSettings: ProducerSettings[String, Array[Byte]] = - ProducerSettings(config, new StringSerializer, new ByteArraySerializer) - .withBootstrapServers(producerConfig.bootstrapServers) - - val nrUsers = 200 - val maxPrice = 10000 - val maxQuantity = 5 - val products = List("cat t-shirt", "pekko t-shirt", "skis", "climbing shoes", "rope") - - val done: Future[Done] = - Source - .tick(1.second, 1.second, "tick") - .map(_ => { - val randomEntityId = Random.nextInt(nrUsers).toString - val price = Random.nextInt(maxPrice) - val quantity = Random.nextInt(maxQuantity) - val product = products(Random.nextInt(products.size)) - val message = UserPurchaseProto(randomEntityId, product, quantity, price).toByteArray - log.info("Sending message to user {}", randomEntityId) - // rely on the default kafka partitioner to hash the key and distribute among shards - // the logic of the default partitioner must be replicated in MessageExtractor entityId -> shardId function - new ProducerRecord[String, Array[Byte]](producerConfig.topic, randomEntityId, message) - }) - .runWith(Producer.plainSink(producerSettings)) +object UserEventProducer { + def main(args: Array[String]): Unit = { + + implicit val system: ActorSystem = ActorSystem( + "UserEventProducer", + ConfigFactory.parseString(""" + pekko.actor.provider = "local" + """.stripMargin).withFallback(ConfigFactory.load()).resolve()) + + val log = Logging(system, "UserEventProducer") + + val config = system.settings.config.getConfig("pekko.kafka.producer") + + val producerConfig = ProducerConfig(system.settings.config.getConfig("kafka-to-sharding-producer")) + + val producerSettings: ProducerSettings[String, Array[Byte]] = + ProducerSettings(config, new StringSerializer, new ByteArraySerializer) + .withBootstrapServers(producerConfig.bootstrapServers) + + val nrUsers = 200 + val maxPrice = 10000 + val maxQuantity = 5 + val products = List("cat t-shirt", "pekko t-shirt", "skis", "climbing shoes", "rope") + + val done: Future[Done] = + Source + .tick(1.second, 1.second, "tick") + .map(_ => { + val randomEntityId = Random.nextInt(nrUsers).toString + val price = Random.nextInt(maxPrice) + val quantity = Random.nextInt(maxQuantity) + val product = products(Random.nextInt(products.size)) + val message = UserPurchaseProto(randomEntityId, product, quantity, price).toByteArray + log.info("Sending message to user {}", randomEntityId) + // rely on the default kafka partitioner to hash the key and distribute among shards + // the logic of the default partitioner must be replicated in MessageExtractor entityId -> shardId function + new ProducerRecord[String, Array[Byte]](producerConfig.topic, randomEntityId, message) + }) + .runWith(Producer.plainSink(producerSettings)) + } }