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 }