github.com/stackb/rules_proto@v0.0.0-20240221195024-5428336c51f1/example/routeguide/scala/RouteGuideClient.scala (about)

     1  /**
     2   * Copied from https://github.com/btlines/grpcexample/blob/master/src/main/scala/io/grpc/routeguide/RouteGuideClient.scala
     3   * License: MIT
     4   */
     5  package example.routeguide.scala
     6  
     7  import java.io.IOException
     8  import java.util.concurrent.{CountDownLatch, TimeUnit}
     9  import java.util.logging.Logger
    10  import io.grpc.{ManagedChannelBuilder, Status}
    11  import io.grpc.stub.StreamObserver
    12  import scala.io.StdIn
    13  import scala.util.{Random, Try, Success, Failure}
    14  
    15  import example.routeguide.{Feature, Point, RouteNote, RouteSummary, Rectangle, RouteGuideGrpc}
    16  
    17  class RouteGuideClient(host: String, port: Int) {
    18  
    19    val logger: Logger = Logger.getLogger(classOf[RouteGuideClient].getName)
    20  
    21    val channel =
    22      ManagedChannelBuilder
    23        .forAddress(host, port)
    24        .usePlaintext()
    25        .build()
    26  
    27    val blockingStub = RouteGuideGrpc.blockingStub(channel)
    28    val asyncStub = RouteGuideGrpc.stub(channel)
    29  
    30    def shutdown(): Unit = channel.shutdown().awaitTermination(5, TimeUnit.SECONDS)
    31  
    32    import io.grpc.StatusRuntimeException
    33  
    34    /**
    35      * Blocking unary call example.  Calls getFeature and prints the response.
    36      */
    37    def getFeature(lat: Int, lon: Int): Unit = {
    38      logger.info(s"*** GetFeature: lat=$lat lon=$lon")
    39      val request = Point(lat, lon)
    40      try {
    41        val feature = blockingStub.getFeature(request)
    42        val lat = RouteGuideServiceUtil.getLatitude(feature.getLocation)
    43        val lon =  RouteGuideServiceUtil.getLongitude(feature.getLocation)
    44        if (RouteGuideServiceUtil.isValid(feature)) {
    45          logger.info(s"Found feature called '${feature.name}' at $lat, $lon")
    46        } else {
    47          logger.info(s"Found no feature at $lat, $lon")
    48        }
    49      } catch {
    50        case e: StatusRuntimeException =>
    51          logger.warning(s"RPC failed:${e.getStatus}")
    52          sys.exit(1)
    53      }
    54    }
    55  
    56    import io.grpc.StatusRuntimeException
    57  
    58    /**
    59      * Blocking server-streaming example. Calls listFeatures with a rectangle of interest. Prints each
    60      * response feature as it arrives.
    61      */
    62    def listFeatures(lowLat: Int, lowLon: Int, hiLat: Int, hiLon: Int): Unit = {
    63      logger.info(s"*** ListFeatures: lowLat=$lowLat lowLon=$lowLon hiLat=$hiLat hiLon=$hiLon")
    64      val request = Rectangle(
    65        lo = Some(Point(lowLat, lowLon)),
    66        hi = Some(Point(hiLat, hiLon))
    67      )
    68      try {
    69        val features = blockingStub.listFeatures(request)
    70        features.zipWithIndex.foreach { case (feature, index) =>
    71          logger.info(s"Result #$index: $feature")
    72        }
    73      } catch {
    74        case e: StatusRuntimeException =>
    75          logger.warning(s"RPC failed: ${e.getStatus}")
    76          sys.exit(1)
    77      }
    78    }
    79  
    80    /**
    81      * Async client-streaming example. Sends {@code numPoints} randomly chosen points from {@code
    82      * features} with a variable delay in between. Prints the statistics when they are sent from the
    83      * server.
    84      */
    85    @throws[InterruptedException]
    86    def recordRoute(features: Seq[Feature], numPoints: Int): Unit = {
    87      logger.info("*** RecordRoute features=" + features.size)
    88      val finishLatch = new CountDownLatch(1)
    89      val responseObserver = new StreamObserver[RouteSummary]() {
    90        override def onNext(summary: RouteSummary): Unit = {
    91          logger.info(s"Finished trip with ${summary.pointCount} points. Passed ${summary.featureCount} features. " + s"Travelled ${summary.distance} meters. It took ${summary.elapsedTime} seconds.")
    92        }
    93  
    94        override def onError(t: Throwable): Unit = {
    95          logger.warning(s"RecordRoute Failed: ${Status.fromThrowable(t)}")
    96          finishLatch.countDown()
    97          sys.exit(1)
    98        }
    99  
   100        override def onCompleted(): Unit = {
   101          logger.info("Finished RecordRoute")
   102          finishLatch.countDown()
   103        }
   104      }
   105      val requestObserver = asyncStub.recordRoute(responseObserver)
   106      try { // Send numPoints points randomly selected from the features list.
   107        (0 to numPoints).foreach { i =>
   108          if (finishLatch.getCount > 0) {
   109            val index = Random.nextInt(features.size)
   110            val point = features(index).getLocation
   111            logger.info(s"Visiting point ${point.latitude}, ${point.longitude}")
   112            requestObserver.onNext(point)
   113            // Sleep for a bit before sending the next one.
   114            Thread.sleep(Random.nextInt(1000) + 500)
   115          }
   116        }
   117      } catch {
   118        case e: RuntimeException =>
   119          // Cancel RPC
   120          requestObserver.onError(e)
   121          throw e
   122      }
   123      // Mark the end of requests
   124      requestObserver.onCompleted()
   125      // Receiving happens asynchronously
   126      if (!finishLatch.await(1, TimeUnit.MINUTES)) logger.warning("recordRoute can not finish within 1 minutes")
   127    }
   128  
   129    /**
   130      * Bi-directional example, which can only be asynchronous. Send some chat messages, and print any
   131      * chat messages that are sent from the server.
   132      */
   133    def routeChat: CountDownLatch = {
   134      logger.info("*** RouteChat")
   135      val finishLatch = new CountDownLatch(1)
   136      val requestObserver = asyncStub.routeChat(new StreamObserver[RouteNote]() {
   137        override def onNext(note: RouteNote): Unit = {
   138          logger.info(s"Got message '${note.message}' at ${note.getLocation.latitude}, ${note.getLocation.longitude}")
   139        }
   140  
   141        override def onError(t: Throwable): Unit = {
   142          logger.warning(s"RouteChat Failed: ${Status.fromThrowable(t)}")
   143          finishLatch.countDown()
   144          sys.exit(1)
   145        }
   146  
   147        override def onCompleted(): Unit = {
   148          logger.info("Finished RouteChat")
   149          finishLatch.countDown()
   150        }
   151      })
   152      try {
   153        val requests = Seq(
   154          RouteNote(message = "First message", location = Some(Point(0, 0))),
   155          RouteNote(message = "Second message", location = Some(Point(0, 1))),
   156          RouteNote(message = "Third message", location = Some(Point(1, 0))),
   157          RouteNote(message = "Fourth message", location = Some(Point(1, 1)))
   158        )
   159        for (request <- requests) {
   160          logger.info(s"Sending message '${request.message}' at ${request.getLocation.latitude}, ${request.getLocation.longitude}")
   161          requestObserver.onNext(request)
   162        }
   163      } catch {
   164        case e: RuntimeException =>
   165          // Cancel RPC
   166          requestObserver.onError(e)
   167          throw e
   168      }
   169      // Mark the end of requests
   170      requestObserver.onCompleted()
   171      // return the latch while receiving happens asynchronously
   172      finishLatch
   173    }
   174  
   175  }
   176  
   177  object RouteGuideClient extends App {
   178    val logger = Logger.getLogger(getClass.getName)
   179  
   180  // This isn't working due to some issue with the json4s library - not a priority to solve it
   181  //
   182  //  val features: Seq[Feature] = Try {
   183  //    RouteGuidePersistence.parseFeatures(RouteGuidePersistence.defaultFeatureFile)
   184  //  } recoverWith {
   185  //    case e: Throwable =>
   186  //      logger.warning("features load failed: " + e)
   187  //      Failure(e)
   188  //  } getOrElse {
   189  //    logger.warning("Can't load feature list from file")
   190  //    Seq.empty
   191  //  }
   192  
   193    val features = RouteGuideUtil.getFeatures
   194  
   195    val port = if (System.getenv("SERVER_PORT") != null) {
   196      System.getenv("SERVER_PORT").toInt
   197    } else 50056
   198  
   199    val client = new RouteGuideClient("localhost", port)
   200    var stop = false
   201  
   202    try {
   203      client.getFeature(409146138, -746188906)
   204      client.listFeatures(400000000, -750000000, 420000000, -730000000)
   205      client.recordRoute(features, 2)
   206      val finishLatch = client.routeChat
   207      if (!finishLatch.await(1, TimeUnit.MINUTES)) logger.warning("routeChat can not finish within 1 minutes")
   208    } finally client.shutdown()
   209  
   210  }