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

     1  /*
     2   * Copyright 2015 The gRPC Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package example.routeguide.java;
    18  
    19  import static java.lang.Math.atan2;
    20  import static java.lang.Math.cos;
    21  import static java.lang.Math.max;
    22  import static java.lang.Math.min;
    23  import static java.lang.Math.sin;
    24  import static java.lang.Math.sqrt;
    25  import static java.lang.Math.toRadians;
    26  import static java.util.concurrent.TimeUnit.NANOSECONDS;
    27  
    28  import io.grpc.Server;
    29  import io.grpc.ServerBuilder;
    30  import io.grpc.stub.StreamObserver;
    31  import java.io.IOException;
    32  import java.net.URL;
    33  import java.util.ArrayList;
    34  import java.util.Collection;
    35  import java.util.Collections;
    36  import java.util.List;
    37  import java.util.concurrent.ConcurrentHashMap;
    38  import java.util.concurrent.ConcurrentMap;
    39  import java.util.logging.Level;
    40  import java.util.logging.Logger;
    41  
    42  import example.routeguide.Feature;
    43  import example.routeguide.Point;
    44  import example.routeguide.Rectangle;
    45  import example.routeguide.RouteGuideGrpc;
    46  import example.routeguide.RouteNote;
    47  import example.routeguide.RouteSummary;
    48  
    49  /**
    50   * A sample gRPC server that serve the RouteGuide (see route_guide.proto) service.
    51   */
    52  public class RouteGuideServer {
    53    private static final Logger logger = Logger.getLogger(RouteGuideServer.class.getName());
    54  
    55    private final int port;
    56    private final Server server;
    57  
    58    public RouteGuideServer(int port) throws IOException {
    59      this(port, RouteGuideUtil.getDefaultFeaturesFile());
    60    }
    61  
    62    /** Create a RouteGuide server listening on {@code port} using {@code featureFile} database. */
    63    public RouteGuideServer(int port, URL featureFile) throws IOException {
    64      this(ServerBuilder.forPort(port), port, RouteGuideUtil.parseFeatures(featureFile));
    65    }
    66  
    67    /** Create a RouteGuide server using serverBuilder as a base and features as data. */
    68    public RouteGuideServer(ServerBuilder<?> serverBuilder, int port, Collection<Feature> features) {
    69      this.port = port;
    70      logger.info("Initializing server with " + features.size() + " features");
    71      server = serverBuilder.addService(new RouteGuideService(features))
    72          .build();
    73    }
    74  
    75    /** Start serving requests. */
    76    public void start() throws IOException {
    77      server.start();
    78      logger.info("Java server started, listening on " + port);
    79      Runtime.getRuntime().addShutdownHook(new Thread() {
    80        @Override
    81        public void run() {
    82          // Use stderr here since the logger may has been reset by its JVM shutdown hook.
    83          System.err.println("*** shutting down gRPC server since JVM is shutting down");
    84          RouteGuideServer.this.stop();
    85          System.err.println("*** server shut down");
    86        }
    87      });
    88    }
    89  
    90    /** Stop serving requests and shutdown resources. */
    91    public void stop() {
    92      if (server != null) {
    93        server.shutdown();
    94      }
    95    }
    96  
    97    /**
    98     * Await termination on the main thread since the grpc library uses daemon threads.
    99     */
   100    private void blockUntilShutdown() throws InterruptedException {
   101      if (server != null) {
   102        server.awaitTermination();
   103      }
   104    }
   105  
   106    /**
   107     * Main method.  This comment makes the linter happy.
   108     */
   109    public static void main(String[] args) throws Exception {
   110      int port = 50074;
   111      String serverPort = System.getenv("SERVER_PORT");
   112      if (serverPort != null && !serverPort.isEmpty()) {
   113        port = Integer.parseInt(serverPort);
   114      }
   115      RouteGuideServer server = new RouteGuideServer(port);
   116      server.start();
   117      server.blockUntilShutdown();
   118    }
   119  
   120    /**
   121     * Our implementation of RouteGuide service.
   122     *
   123     * <p>See route_guide.proto for details of the methods.
   124     */
   125    private static class RouteGuideService extends RouteGuideGrpc.RouteGuideImplBase {
   126      private final Collection<Feature> features;
   127      private final ConcurrentMap<Point, List<RouteNote>> routeNotes =
   128          new ConcurrentHashMap<Point, List<RouteNote>>();
   129  
   130      RouteGuideService(Collection<Feature> features) {
   131        this.features = features;
   132      }
   133  
   134      /**
   135       * Gets the {@link Feature} at the requested {@link Point}. If no feature at that location
   136       * exists, an unnamed feature is returned at the provided location.
   137       *
   138       * @param request the requested location for the feature.
   139       * @param responseObserver the observer that will receive the feature at the requested point.
   140       */
   141      @Override
   142      public void getFeature(Point request, StreamObserver<Feature> responseObserver) {
   143        responseObserver.onNext(checkFeature(request));
   144        responseObserver.onCompleted();
   145      }
   146  
   147      /**
   148       * Gets all features contained within the given bounding {@link Rectangle}.
   149       *
   150       * @param request the bounding rectangle for the requested features.
   151       * @param responseObserver the observer that will receive the features.
   152       */
   153      @Override
   154      public void listFeatures(Rectangle request, StreamObserver<Feature> responseObserver) {
   155        int left = min(request.getLo().getLongitude(), request.getHi().getLongitude());
   156        int right = max(request.getLo().getLongitude(), request.getHi().getLongitude());
   157        int top = max(request.getLo().getLatitude(), request.getHi().getLatitude());
   158        int bottom = min(request.getLo().getLatitude(), request.getHi().getLatitude());
   159  
   160        for (Feature feature : features) {
   161          if (!RouteGuideUtil.exists(feature)) {
   162            continue;
   163          }
   164  
   165          int lat = feature.getLocation().getLatitude();
   166          int lon = feature.getLocation().getLongitude();
   167          if (lon >= left && lon <= right && lat >= bottom && lat <= top) {
   168            responseObserver.onNext(feature);
   169          }
   170        }
   171        responseObserver.onCompleted();
   172      }
   173  
   174      /**
   175       * Gets a stream of points, and responds with statistics about the "trip": number of points,
   176       * number of known features visited, total distance traveled, and total time spent.
   177       *
   178       * @param responseObserver an observer to receive the response summary.
   179       * @return an observer to receive the requested route points.
   180       */
   181      @Override
   182      public StreamObserver<Point> recordRoute(final StreamObserver<RouteSummary> responseObserver) {
   183        return new StreamObserver<Point>() {
   184          int pointCount;
   185          int featureCount;
   186          int distance;
   187          Point previous;
   188          final long startTime = System.nanoTime();
   189  
   190          @Override
   191          public void onNext(Point point) {
   192            pointCount++;
   193            if (RouteGuideUtil.exists(checkFeature(point))) {
   194              featureCount++;
   195            }
   196            // For each point after the first, add the incremental distance from the previous point to
   197            // the total distance value.
   198            if (previous != null) {
   199              distance += calcDistance(previous, point);
   200            }
   201            previous = point;
   202          }
   203  
   204          @Override
   205          public void onError(Throwable t) {
   206            logger.log(Level.WARNING, "recordRoute cancelled");
   207          }
   208  
   209          @Override
   210          public void onCompleted() {
   211            long seconds = NANOSECONDS.toSeconds(System.nanoTime() - startTime);
   212            responseObserver.onNext(RouteSummary.newBuilder().setPointCount(pointCount)
   213                .setFeatureCount(featureCount).setDistance(distance)
   214                .setElapsedTime((int) seconds).build());
   215            responseObserver.onCompleted();
   216          }
   217        };
   218      }
   219  
   220      /**
   221       * Receives a stream of message/location pairs, and responds with a stream of all previous
   222       * messages at each of those locations.
   223       *
   224       * @param responseObserver an observer to receive the stream of previous messages.
   225       * @return an observer to handle requested message/location pairs.
   226       */
   227      @Override
   228      public StreamObserver<RouteNote> routeChat(final StreamObserver<RouteNote> responseObserver) {
   229        return new StreamObserver<RouteNote>() {
   230          @Override
   231          public void onNext(RouteNote note) {
   232            List<RouteNote> notes = getOrCreateNotes(note.getLocation());
   233  
   234            // Respond with all previous notes at this location.
   235            for (RouteNote prevNote : notes.toArray(new RouteNote[0])) {
   236              responseObserver.onNext(prevNote);
   237            }
   238  
   239            // Now add the new note to the list
   240            notes.add(note);
   241          }
   242  
   243          @Override
   244          public void onError(Throwable t) {
   245            logger.log(Level.WARNING, "routeChat cancelled");
   246          }
   247  
   248          @Override
   249          public void onCompleted() {
   250            responseObserver.onCompleted();
   251          }
   252        };
   253      }
   254  
   255      /**
   256       * Get the notes list for the given location. If missing, create it.
   257       */
   258      private List<RouteNote> getOrCreateNotes(Point location) {
   259        List<RouteNote> notes = Collections.synchronizedList(new ArrayList<RouteNote>());
   260        List<RouteNote> prevNotes = routeNotes.putIfAbsent(location, notes);
   261        return prevNotes != null ? prevNotes : notes;
   262      }
   263  
   264      /**
   265       * Gets the feature at the given point.
   266       *
   267       * @param location the location to check.
   268       * @return The feature object at the point. Note that an empty name indicates no feature.
   269       */
   270      private Feature checkFeature(Point location) {
   271        for (Feature feature : features) {
   272          if (feature.getLocation().getLatitude() == location.getLatitude()
   273              && feature.getLocation().getLongitude() == location.getLongitude()) {
   274            return feature;
   275          }
   276        }
   277  
   278        // No feature was found, return an unnamed feature.
   279        return Feature.newBuilder().setName("").setLocation(location).build();
   280      }
   281  
   282      /**
   283       * Calculate the distance between two points using the "haversine" formula.
   284       * The formula is based on http://mathforum.org/library/drmath/view/51879.html.
   285       *
   286       * @param start The starting point
   287       * @param end The end point
   288       * @return The distance between the points in meters
   289       */
   290      private static int calcDistance(Point start, Point end) {
   291        int r = 6371000; // earth radius in meters
   292        double lat1 = toRadians(RouteGuideUtil.getLatitude(start));
   293        double lat2 = toRadians(RouteGuideUtil.getLatitude(end));
   294        double lon1 = toRadians(RouteGuideUtil.getLongitude(start));
   295        double lon2 = toRadians(RouteGuideUtil.getLongitude(end));
   296        double deltaLat = lat2 - lat1;
   297        double deltaLon = lon2 - lon1;
   298  
   299        double a = sin(deltaLat / 2) * sin(deltaLat / 2)
   300            + cos(lat1) * cos(lat2) * sin(deltaLon / 2) * sin(deltaLon / 2);
   301        double c = 2 * atan2(sqrt(a), sqrt(1 - a));
   302  
   303        return (int) (r * c);
   304      }
   305    }
   306  }