diff --git a/build.gradle b/build.gradle index 7b3cebf3d4..74c39df35b 100644 --- a/build.gradle +++ b/build.gradle @@ -20,16 +20,17 @@ subprojects { ext { libraries = [ - protobuf: 'com.google.protobuf:protobuf-java:3.0.0-pre', guava: 'com.google.guava:guava:18.0', - jsr305: 'com.google.code.findbugs:jsr305:3.0.0', - oauth_client: 'com.google.oauth-client:google-oauth-client:1.18.0-rc', - javaee_api: 'javax:javaee-api:7.0', - hpack: 'com.twitter:hpack:0.9.1', - protobuf_plugin: 'ws.antonov.gradle.plugins:gradle-plugin-protobuf:0.9.1', - okhttp: 'com.squareup.okhttp:okhttp:2.2.0', // used to collect benchmark results hdrhistogram: 'org.hdrhistogram:HdrHistogram:2.1.4', + hpack: 'com.twitter:hpack:0.9.1', + javaee_api: 'javax:javaee-api:7.0', + jsonp: 'org.glassfish:javax.json:1.0.4', + jsr305: 'com.google.code.findbugs:jsr305:3.0.0', + oauth_client: 'com.google.oauth-client:google-oauth-client:1.18.0-rc', + okhttp: 'com.squareup.okhttp:okhttp:2.2.0', + protobuf: 'com.google.protobuf:protobuf-java:3.0.0-pre', + protobuf_plugin: 'ws.antonov.gradle.plugins:gradle-plugin-protobuf:0.9.1', // TODO: Unreleased dependencies. // These must already be installed in the local maven repository. diff --git a/examples/README.md b/examples/README.md index 8832bb0ae3..138a4d9587 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,22 +1,19 @@ grpc Examples ============================================== -In order to run the examples simply execute one of the gradle tasks `mathserver`, `mathclient`, -`stockserver` or `stockclient`. +In order to run the examples simply execute one of the gradle tasks `routeGuideServer` or +`routeGuideClient`. -For example, say you want to play around with the math examples. First you want to start -the server and then have the client connect to it and do some fun calculations. - -Assuming you are in the grpc-java root folder you would first start the math server by running +Assuming you are in the grpc-java root folder you would first start the server by running ``` -$ ./gradlew :grpc-examples:mathserver +$ ./gradlew :grpc-examples:routeGuideServer ``` and in a different terminal window then run the client by typing ``` -$ ./gradlew :grpc-examples:mathclient +$ ./gradlew :grpc-examples:routeGuideClient ``` That's it! diff --git a/examples/build.gradle b/examples/build.gradle index f9f7eb73b6..07eeac8891 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -16,32 +16,21 @@ dependencies { compile project(':grpc-core'), project(':grpc-netty'), project(':grpc-okhttp'), - project(':grpc-stub') + project(':grpc-stub'), + libraries.jsonp } protobufCodeGenPlugins = ["java_plugin:$rootDir/compiler/build/binaries/java_pluginExecutable/java_plugin"] generateProto.dependsOn ':grpc-compiler:java_pluginExecutable' -task mathserver(type: JavaExec) { - main = "io.grpc.examples.MathServer" - description = "Executes the math server." +task routeGuideServer(type: JavaExec) { + main = "io.grpc.examples.RouteGuideServer" + description = "Executes the route guide server." classpath = sourceSets.main.runtimeClasspath } -task mathclient(type: JavaExec) { - main = "io.grpc.examples.MathClient" - description = "Executes the math client." - classpath = sourceSets.main.runtimeClasspath -} - -task stockserver(type: JavaExec) { - main = "io.grpc.examples.StockServer" - description = "Executes the stock server." - classpath = sourceSets.main.runtimeClasspath -} - -task stockclient(type: JavaExec) { - main = "io.grpc.examples.StockClient" - description = "Executes the stock client." +task routeGuideClient(type: JavaExec) { + main = "io.grpc.examples.RouteGuideClient" + description = "Executes the route guide client." classpath = sourceSets.main.runtimeClasspath } diff --git a/examples/src/main/java/io/grpc/examples/MathClient.java b/examples/src/main/java/io/grpc/examples/MathClient.java deleted file mode 100644 index d0986d762a..0000000000 --- a/examples/src/main/java/io/grpc/examples/MathClient.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2014, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package io.grpc.examples; - -import com.google.common.util.concurrent.SettableFuture; -import io.grpc.examples.CalcGrpc; -import io.grpc.examples.CalcGrpc.CalcBlockingStub; -import io.grpc.examples.CalcGrpc.CalcStub; -import io.grpc.examples.Math.DivArgs; -import io.grpc.examples.Math.DivReply; -import io.grpc.examples.Math.FibArgs; -import io.grpc.examples.Math.Num; - -import io.grpc.ChannelImpl; -import io.grpc.stub.StreamObserver; -import io.grpc.transport.netty.NegotiationType; -import io.grpc.transport.netty.NettyChannelBuilder; - -import java.util.Iterator; -import java.util.Random; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Sample client code that makes gRPC calls to the server. - */ -public class MathClient { - private final Logger logger = Logger.getLogger(MathClient.class.getName()); - private final Random rand = new Random(); - private final ChannelImpl channel; - private final CalcBlockingStub blockingStub; - private final CalcStub asyncStub; - - public MathClient(String host, int port) { - channel = NettyChannelBuilder.forAddress(host, port) - .negotiationType(NegotiationType.PLAINTEXT) - .build(); - blockingStub = CalcGrpc.newBlockingStub(channel); - asyncStub = CalcGrpc.newStub(channel); - } - - public void shutdown() throws InterruptedException { - channel.shutdown().awaitTerminated(5, TimeUnit.SECONDS); - } - - /** - * This example shows how to make a blocking unary call. - */ - public void blockingDiv(long dividend, long divisor) { - logger.info("*** Blocking Div: Dividend=" + dividend + ", Divisor=" + divisor); - DivReply reply; - try { - reply = blockingStub.div( - DivArgs.newBuilder().setDividend(dividend).setDivisor(divisor).build()); - } catch (RuntimeException e) { - logger.log(Level.WARNING, "RPC failed", e); - return; - } - logger.info("Result: Quotient=" + reply.getQuotient() + ", remainder=" + reply.getRemainder()); - } - - /** - * This example shows how to make an asynchronous unary call. - */ - public void asyncDiv(long dividend, long divisor) { - logger.info("*** Async Div: Dividend=" + dividend + ", Divisor=" + divisor); - DivArgs request = DivArgs.newBuilder().setDividend(dividend).setDivisor(divisor).build(); - final SettableFuture responseFuture = SettableFuture.create(); - asyncStub.div(request, new StreamObserver (){ - DivReply reply; - @Override - public void onValue(DivReply value) { - reply = value; - } - - @Override - public void onError(Throwable t) { - responseFuture.setException(t); - } - - @Override - public void onCompleted() { - responseFuture.set(reply); - } - }); - - logger.info("Waiting for result..."); - try { - DivReply reply = responseFuture.get(); - logger.info( - "Result: Quotient=" + reply.getQuotient() + ", remainder=" + reply.getRemainder()); - } catch (InterruptedException e) { - logger.log(Level.WARNING, "Interrupted while waiting for result...", e); - } catch (ExecutionException e) { - logger.log(Level.WARNING, "RPC failed", e); - } - } - - /** - * This example shows how to make a bi-directional streaming call, which can only be asynchronous. - */ - public void divMany() { - logger.info("*** DivMany"); - final SettableFuture finishFuture = SettableFuture.create(); - StreamObserver requestSink = asyncStub.divMany(new StreamObserver() { - int count = 0; - @Override - public void onValue(DivReply value) { - count++; - logger.info("Result " + count + ": Quotient=" + value.getQuotient() + ", remainder=" - + value.getRemainder()); - } - - @Override - public void onError(Throwable t) { - finishFuture.setException(t); - } - - @Override - public void onCompleted() { - finishFuture.set(null); - } - }); - - int count = 5; - for (int i = 0; i < count; i++) { - long dividend = rand.nextLong(); - long divisor = rand.nextInt() + 1; // plus 1 to avoid 0 divisor. - logger.info("Request " + (i + 1) + ": Dividend=" + dividend + ", Divisor=%d" + divisor); - requestSink.onValue( - DivArgs.newBuilder().setDividend(dividend).setDivisor(divisor).build()); - } - requestSink.onCompleted(); - - try { - finishFuture.get(); - logger.info("Finished " + count + " div requests"); - } catch (InterruptedException e) { - logger.log(Level.WARNING, "Interrupted while waiting for result...", e); - } catch (ExecutionException e) { - logger.log(Level.WARNING, "RPC failed", e); - } - } - - /** - * This example shows how to make a client streaming call. - */ - public void sum() throws InterruptedException { - final CountDownLatch completed = new CountDownLatch(1); - logger.info("*** Sum"); - int count = 5; - StreamObserver responseObserver = new StreamObserver() { - @Override - public void onValue(Num value) { - logger.info("Sum=" + value); - } - - @Override - public void onError(Throwable t) { - logger.log(Level.SEVERE, "Error receiving response", t); - } - - @Override - public void onCompleted() { - completed.countDown(); - } - }; - StreamObserver requestObserver = asyncStub.sum(responseObserver); - StringBuilder numMsg = new StringBuilder(); - for (int i = 0; i < count; i++) { - int value = rand.nextInt(); - requestObserver.onValue(Num.newBuilder().setNum(value).build()); - numMsg.append(value); - if (i != count - 1) { - numMsg.append(" + "); - } - } - logger.info(numMsg.toString()); - requestObserver.onCompleted(); - completed.await(); - } - - /** - * This example shows how to make a blocking server streaming call. - * - *

The asynchronous usage is similar to {@link #divMany}. - */ - public void blockingFib() { - // TODO(madongfly): Support "send until cancel". Currently, client application can not - // cancel a server streaming call. - int limit = rand.nextInt(20) + 10; - logger.info("*** Blocking Fib, print the first " + limit + " fibonacci numbers."); - Iterator iterator = blockingStub.fib(FibArgs.newBuilder().setLimit(limit).build()); - StringBuilder resultMsg = new StringBuilder(); - int realCount = 0; - while (iterator.hasNext()) { - realCount++; - resultMsg.append(iterator.next().getNum()); - resultMsg.append(", "); - } - logger.info("The first " + realCount + " fibonacci numbers: " + resultMsg.toString()); - } - - public static void main(String[] args) throws InterruptedException { - MathClient client = new MathClient("localhost", 8980); - try { - client.blockingDiv(2014, 4); - // Note: This call should be failed, but we can not get the server specified error info - // currently, so the error message logged is not clear. - client.blockingDiv(73, 0); - client.asyncDiv(1986, 12); - client.divMany(); - client.sum(); - client.blockingFib(); - } finally { - client.shutdown(); - } - } -} diff --git a/examples/src/main/java/io/grpc/examples/MathServer.java b/examples/src/main/java/io/grpc/examples/MathServer.java deleted file mode 100644 index 6285e294b4..0000000000 --- a/examples/src/main/java/io/grpc/examples/MathServer.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2014, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package io.grpc.examples; - -import io.grpc.examples.CalcGrpc; -import io.grpc.examples.Math.DivArgs; -import io.grpc.examples.Math.DivReply; -import io.grpc.examples.Math.FibArgs; -import io.grpc.examples.Math.Num; - -import io.grpc.ServerImpl; -import io.grpc.stub.StreamObserver; -import io.grpc.transport.netty.NettyServerBuilder; - -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * A sample gRPC server that serve the Calc (see math.proto) service. - */ -public class MathServer { - private final Logger logger = Logger.getLogger(MathServer.class.getName()); - private final int port; - private ServerImpl gRpcServer; - - public MathServer(int port) { - this.port = port; - } - - public void start() { - gRpcServer = NettyServerBuilder.forPort(port) - .addService(CalcGrpc.bindService(new CalcService())) - .build().start(); - logger.info("Server started, listening on " + port); - // TODO(madongfly): gRPC server should register JVM shutdown hook to shutdown itself, remove this - // after we support that. - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - // Use stderr here since the logger may has been reset by its JVM shutdown hook. - System.err.println("*** shutting down gRPC server since JVM is shutting down"); - MathServer.this.stop(); - System.err.println("*** server shut down"); - } - }); - } - - public void stop() { - if (gRpcServer != null) { - gRpcServer.shutdown(); - } - } - - public static void main(String[] args) throws Exception { - MathServer server = new MathServer(8980); - server.start(); - } - - /** - * Our implementation of Calc service. - * - *

See math.proto for details of the methods. - */ - private class CalcService implements CalcGrpc.Calc { - - @Override - public void div(DivArgs request, StreamObserver responseObserver) { - if (request.getDivisor() == 0) { - responseObserver.onError(new IllegalArgumentException("divisor is 0")); - return; - } - responseObserver.onValue(div(request)); - responseObserver.onCompleted(); - } - - @Override - public StreamObserver divMany(final StreamObserver responseObserver) { - return new StreamObserver() { - @Override - public void onValue(DivArgs value) { - if (value.getDivisor() == 0) { - responseObserver.onError(new IllegalArgumentException("divisor is 0")); - return; - } - responseObserver.onValue(div(value)); - } - - @Override - public void onError(Throwable t) { - logger.log(Level.WARNING, "Encountered error in divMany", t); - } - - @Override - public void onCompleted() { - responseObserver.onCompleted(); - }}; - } - - @Override - public void fib(FibArgs request, StreamObserver responseObserver) { - int limit = (int) request.getLimit(); - if (limit <= 0) { - // TODO(madongfly): Support "send until cancel". Currently, client application can not - // cancel a server streaming call. - return; - } - long preLast = 1; - long last = 1; - for (int i = 0; i < limit; i++) { - if (i < 2) { - responseObserver.onValue(Num.newBuilder().setNum(1).build()); - } else { - long fib = preLast + last; - responseObserver.onValue(Num.newBuilder().setNum(fib).build()); - preLast = last; - last = fib; - } - } - responseObserver.onCompleted(); - } - - @Override - public StreamObserver sum(final StreamObserver responseObserver) { - return new StreamObserver() { - long sum = 0; - - @Override - public void onValue(Num value) { - sum += value.getNum(); - } - - @Override - public void onError(Throwable t) { - logger.log(Level.WARNING, "Encountered error in sum", t); - } - - @Override - public void onCompleted() { - responseObserver.onValue(Num.newBuilder().setNum(sum).build()); - responseObserver.onCompleted(); - } - }; - } - - private DivReply div(DivArgs request) { - long divisor = request.getDivisor(); - long dividend = request.getDividend(); - return DivReply.newBuilder() - .setQuotient(dividend / divisor) - .setRemainder(dividend % divisor) - .build(); - } - } -} diff --git a/examples/src/main/java/io/grpc/examples/RouteGuideClient.java b/examples/src/main/java/io/grpc/examples/RouteGuideClient.java new file mode 100644 index 0000000000..acd0642361 --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/RouteGuideClient.java @@ -0,0 +1,255 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.examples; + +import com.google.common.util.concurrent.SettableFuture; + +import io.grpc.ChannelImpl; +import io.grpc.examples.RouteGuideGrpc.RouteGuideBlockingStub; +import io.grpc.examples.RouteGuideGrpc.RouteGuideStub; +import io.grpc.examples.RouteGuideOuterClass.Feature; +import io.grpc.examples.RouteGuideOuterClass.Point; +import io.grpc.examples.RouteGuideOuterClass.Rectangle; +import io.grpc.examples.RouteGuideOuterClass.RouteNote; +import io.grpc.examples.RouteGuideOuterClass.RouteSummary; +import io.grpc.stub.StreamObserver; +import io.grpc.transport.netty.NegotiationType; +import io.grpc.transport.netty.NettyChannelBuilder; + +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Sample client code that makes gRPC calls to the server. + */ +public class RouteGuideClient { + private static final Logger logger = Logger.getLogger(RouteGuideClient.class.getName()); + + private final ChannelImpl channel; + private final RouteGuideBlockingStub blockingStub; + private final RouteGuideStub asyncStub; + + public RouteGuideClient(String host, int port) { + channel = NettyChannelBuilder.forAddress(host, port) + .negotiationType(NegotiationType.PLAINTEXT) + .build(); + blockingStub = RouteGuideGrpc.newBlockingStub(channel); + asyncStub = RouteGuideGrpc.newStub(channel); + } + + public void shutdown() throws InterruptedException { + channel.shutdown().awaitTerminated(5, TimeUnit.SECONDS); + } + + /** + * This example shows how to make a blocking unary call. + */ + public void getFeature(int lat, int lon) { + try { + info("*** GetFeature: lat={0} lon={1}", lat, lon); + + Point request = Point.newBuilder().setLatitude(lat).setLongitude(lon).build(); + Feature feature = blockingStub.getFeature(request); + if (RouteGuideUtil.exists(feature)) { + info("Found feature called \"{0}\" at {1}, {2}", + feature.getName(), + RouteGuideUtil.getLatitude(feature.getLocation()), + RouteGuideUtil.getLongitude(feature.getLocation())); + } else { + info("Found no feature at {0}, {1}", + RouteGuideUtil.getLatitude(feature.getLocation()), + RouteGuideUtil.getLongitude(feature.getLocation())); + } + } catch (RuntimeException e) { + logger.log(Level.WARNING, "RPC failed", e); + throw e; + } + } + + /** + * This example shows how to make a blocking unary call. + */ + public void listFeatures(int lowLat, int lowLon, int hiLat, int hiLon) { + try { + info("*** ListFeatures: lowLat={0} lowLon={1} hiLat={2} hiLon={3}", lowLat, lowLon, hiLat, + hiLon); + + Rectangle request = + Rectangle.newBuilder() + .setLo(Point.newBuilder().setLatitude(lowLat).setLongitude(lowLon).build()) + .setHi(Point.newBuilder().setLatitude(hiLat).setLongitude(hiLon).build()).build(); + Iterator features = blockingStub.listFeatures(request); + + StringBuilder responseLog = new StringBuilder("Result: "); + while (features.hasNext()) { + Feature feature = features.next(); + responseLog.append(feature); + } + info(responseLog.toString()); + } catch (RuntimeException e) { + logger.log(Level.WARNING, "RPC failed", e); + throw e; + } + } + + public void recordRoute(List features, int numPoints) throws Exception { + info("*** RecordRoute"); + final SettableFuture finishFuture = SettableFuture.create(); + StreamObserver responseObserver = new StreamObserver() { + @Override + public void onValue(RouteSummary summary) { + info("Finished trip with {0} points. Passed {1} features. " + + "Travelled {2} meters. It took {3} seconds.", summary.getPointCount(), + summary.getFeatureCount(), summary.getDistance(), summary.getElapsedTime()); + } + + @Override + public void onError(Throwable t) { + finishFuture.setException(t); + } + + @Override + public void onCompleted() { + finishFuture.set(null); + } + }; + + StreamObserver requestObserver = asyncStub.recordRoute(responseObserver); + try { + // Send numPoints points randomly selected from the features list. + StringBuilder numMsg = new StringBuilder(); + Random rand = new Random(); + for (int i = 0; i < numPoints; ++i) { + int index = rand.nextInt(features.size()); + Point point = features.get(index).getLocation(); + info("Visiting point {0}, {1}", RouteGuideUtil.getLatitude(point), + RouteGuideUtil.getLongitude(point)); + requestObserver.onValue(point); + // Sleep for a bit before sending the next one. + Thread.sleep(rand.nextInt(1000) + 500); + if (finishFuture.isDone()) { + break; + } + } + info(numMsg.toString()); + requestObserver.onCompleted(); + + finishFuture.get(); + info("Finished RecordRoute"); + } catch (Exception e) { + requestObserver.onError(e); + logger.log(Level.WARNING, "RecordRoute Failed", e); + throw e; + } + } + + /** + * This example shows how to make a bi-directional streaming call, which can only be asynchronous. + */ + public void routeChat() throws Exception { + info("*** RoutChat"); + final SettableFuture finishFuture = SettableFuture.create(); + StreamObserver requestObserver = + asyncStub.routeChat(new StreamObserver() { + @Override + public void onValue(RouteNote note) { + info("Got message \"{0}\" at {1}, {2}", note.getMessage(), note.getLocation() + .getLatitude(), note.getLocation().getLongitude()); + } + + @Override + public void onError(Throwable t) { + finishFuture.setException(t); + } + + @Override + public void onCompleted() { + finishFuture.set(null); + } + }); + + try { + RouteNote[] requests = + {newNote("First message", 0, 0), newNote("Second message", 0, 1), + newNote("Third message", 1, 0), newNote("Fourth message", 1, 1)}; + + for (RouteNote request : requests) { + info("Sending message \"{0}\" at {1}, {2}", request.getMessage(), request.getLocation() + .getLatitude(), request.getLocation().getLongitude()); + requestObserver.onValue(request); + } + requestObserver.onCompleted(); + + finishFuture.get(); + info("Finished RouteChat"); + } catch (Exception t) { + requestObserver.onError(t); + logger.log(Level.WARNING, "RouteChat Failed", t); + throw t; + } + } + + public static void main(String[] args) throws Exception { + RouteGuideClient client = new RouteGuideClient("localhost", 8980); + try { + // Looking for a valid feature + client.getFeature(409146138, -746188906); + + // Feature missing. + client.getFeature(0, 0); + + // Looking for features between 40, -75 and 42, -73. + client.listFeatures(400000000, -750000000, 420000000, -730000000); + + // Record a few randomly selected points from the features file. + client.recordRoute(RouteGuideUtil.parseFeatures(RouteGuideUtil.getDefaultFeaturesFile()), 10); + + // Send and receive some notes. + client.routeChat(); + } finally { + client.shutdown(); + } + } + + private static void info(String msg, Object... params) { + logger.log(Level.INFO, msg, params); + } + + private RouteNote newNote(String message, int lat, int lon) { + return RouteNote.newBuilder().setMessage(message) + .setLocation(Point.newBuilder().setLatitude(lat).setLongitude(lon).build()).build(); + } +} diff --git a/examples/src/main/java/io/grpc/examples/RouteGuideServer.java b/examples/src/main/java/io/grpc/examples/RouteGuideServer.java new file mode 100644 index 0000000000..f0fc5d7cc4 --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/RouteGuideServer.java @@ -0,0 +1,302 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.examples; + +import static java.lang.Math.atan2; +import static java.lang.Math.cos; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.lang.Math.sin; +import static java.lang.Math.sqrt; +import static java.lang.Math.toRadians; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import io.grpc.ServerImpl; +import io.grpc.examples.RouteGuideOuterClass.Feature; +import io.grpc.examples.RouteGuideOuterClass.Point; +import io.grpc.examples.RouteGuideOuterClass.Rectangle; +import io.grpc.examples.RouteGuideOuterClass.RouteNote; +import io.grpc.examples.RouteGuideOuterClass.RouteSummary; +import io.grpc.stub.StreamObserver; +import io.grpc.transport.netty.NettyServerBuilder; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A sample gRPC server that serve the RouteGuide (see route_guide.proto) service. + */ +public class RouteGuideServer { + private static final Logger logger = Logger.getLogger(RouteGuideServer.class.getName()); + + private final int port; + private final Collection features; + private ServerImpl gRpcServer; + + public RouteGuideServer(int port) { + this(port, RouteGuideUtil.getDefaultFeaturesFile()); + } + + public RouteGuideServer(int port, URL featureFile) { + try { + this.port = port; + features = RouteGuideUtil.parseFeatures(featureFile); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void start() { + gRpcServer = NettyServerBuilder.forPort(port) + .addService(RouteGuideGrpc.bindService(new RouteGuideService(features))) + .build().start(); + logger.info("Server started, listening on " + port); + // TODO(simonma): gRPC server should register JVM shutdown hook to shutdown itself, remove this + // after we support that. + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + // Use stderr here since the logger may has been reset by its JVM shutdown hook. + System.err.println("*** shutting down gRPC server since JVM is shutting down"); + RouteGuideServer.this.stop(); + System.err.println("*** server shut down"); + } + }); + } + + public void stop() { + if (gRpcServer != null) { + gRpcServer.shutdown(); + } + } + + public static void main(String[] args) throws Exception { + RouteGuideServer server = new RouteGuideServer(8980); + server.start(); + } + + /** + * Our implementation of RouteGuide service. + * + *

See route_guide.proto for details of the methods. + */ + private static class RouteGuideService implements RouteGuideGrpc.RouteGuide { + private final Collection features; + private final ConcurrentMap> routeNotes = + new ConcurrentHashMap>(); + + RouteGuideService(Collection features) { + this.features = features; + } + + /** + * Gets the {@link Feature} at the requested {@link Point}. If no feature at that location + * exists, an unnammed feature is returned at the provided location. + * + * @param request the requested location for the feature. + * @param responseObserver the observer that will receive the feature at the requested point. + */ + @Override + public void getFeature(Point request, StreamObserver responseObserver) { + responseObserver.onValue(getFeature(request)); + responseObserver.onCompleted(); + } + + /** + * Gets all features contained within the given bounding {@link Rectangle}. + * + * @param request the bounding rectangle for the requested features. + * @param responseObserver the observer that will receive the features. + */ + @Override + public void listFeatures(Rectangle request, StreamObserver responseObserver) { + int left = min(request.getLo().getLongitude(), request.getHi().getLongitude()); + int right = max(request.getLo().getLongitude(), request.getHi().getLongitude()); + int top = max(request.getLo().getLatitude(), request.getHi().getLatitude()); + int bottom = min(request.getLo().getLatitude(), request.getHi().getLatitude()); + + for (Feature feature : features) { + if (!RouteGuideUtil.exists(feature)) { + continue; + } + + int lat = feature.getLocation().getLatitude(); + int lon = feature.getLocation().getLongitude(); + if (lon >= left && lon <= right && lat >= bottom && lat <= top) { + responseObserver.onValue(feature); + } + } + responseObserver.onCompleted(); + } + + /** + * Gets a stream of points, and responds with statistics about the "trip": number of points, + * number of known features visited, total distance traveled, and total time spent. + * + * @param responseObserver an observer to receive the response summary. + * @return an observer to receive the requested route points. + */ + @Override + public StreamObserver recordRoute(final StreamObserver responseObserver) { + return new StreamObserver() { + int pointCount; + int featureCount; + int distance; + Point previous; + long startTime = System.nanoTime(); + + @Override + public void onValue(Point point) { + pointCount++; + if (RouteGuideUtil.exists(getFeature(point))) { + featureCount++; + } + // For each point after the first, add the incremental distance from the previous point to + // the total distance value. + if (previous != null) { + distance += calcDistance(previous, point); + } + previous = point; + } + + @Override + public void onError(Throwable t) { + logger.log(Level.WARNING, "Encountered error in recordRoute", t); + } + + @Override + public void onCompleted() { + long seconds = NANOSECONDS.toSeconds(System.nanoTime() - startTime); + responseObserver.onValue(RouteSummary.newBuilder().setPointCount(pointCount) + .setFeatureCount(featureCount).setDistance(distance) + .setElapsedTime((int) seconds).build()); + responseObserver.onCompleted(); + } + }; + } + + /** + * Receives a stream of message/location pairs, and responds with a stream of all previous + * messages at each of those locations. + * + * @param responseObserver an observer to receive the stream of previous messages. + * @return an observer to handle requested message/location pairs. + */ + @Override + public StreamObserver routeChat(final StreamObserver responseObserver) { + return new StreamObserver() { + @Override + public void onValue(RouteNote note) { + List notes = getOrCreateNotes(note.getLocation()); + + // Respond with all previous notes at this location. + for (RouteNote prevNote : notes.toArray(new RouteNote[0])) { + responseObserver.onValue(prevNote); + } + + // Now add the new note to the list + notes.add(note); + } + + @Override + public void onError(Throwable t) { + logger.log(Level.WARNING, "Encountered error in routeChat", t); + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + }; + } + + /** + * Get the notes list for the given location. If missing, create it. + */ + private List getOrCreateNotes(Point location) { + List notes = Collections.synchronizedList(new ArrayList()); + List prevNotes = routeNotes.putIfAbsent(location, notes); + return prevNotes != null ? prevNotes : notes; + } + + /** + * Gets the feature at the given point. + * + * @param location the location to check. + * @return The feature object at the point. Note that an empty name indicates no feature. + */ + private Feature getFeature(Point location) { + for (Feature feature : features) { + if (feature.getLocation().getLatitude() == location.getLatitude() + && feature.getLocation().getLongitude() == location.getLongitude()) { + return feature; + } + } + + // No feature was found, return an unnamed feature. + return Feature.newBuilder().setName("").setLocation(location).build(); + } + + /** + * Calculate the distance between two points using the "haversine" formula. + * This code was taken from http://www.movable-type.co.uk/scripts/latlong.html. + * + * @param start The starting point + * @param end The end point + * @return The distance between the points in meters + */ + private static double calcDistance(Point start, Point end) { + double lat1 = RouteGuideUtil.getLatitude(start); + double lat2 = RouteGuideUtil.getLatitude(end); + double lon1 = RouteGuideUtil.getLongitude(start); + double lon2 = RouteGuideUtil.getLongitude(end); + int R = 6371000; // metres + double φ1 = toRadians(lat1); + double φ2 = toRadians(lat2); + double Δφ = toRadians(lat2-lat1); + double Δλ = toRadians(lon2-lon1); + + double a = sin(Δφ/2) * sin(Δφ/2) + cos(φ1) * cos(φ2) * sin(Δλ/2) * sin(Δλ/2); + double c = 2 * atan2(sqrt(a), sqrt(1-a)); + + return R * c; + } + } +} diff --git a/examples/src/main/java/io/grpc/examples/RouteGuideUtil.java b/examples/src/main/java/io/grpc/examples/RouteGuideUtil.java new file mode 100644 index 0000000000..5e4680b9c2 --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/RouteGuideUtil.java @@ -0,0 +1,111 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.examples; + +import io.grpc.examples.RouteGuideOuterClass.Feature; +import io.grpc.examples.RouteGuideOuterClass.Point; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonValue; + +/** + * Common utilities for the RouteGuide demo. + */ +public class RouteGuideUtil { + private static final double COORD_FACTOR = 1e7; + + /** + * Gets the latitude for the given point. + */ + public static double getLatitude(Point location) { + return location.getLatitude() / COORD_FACTOR; + } + + /** + * Gets the longitude for the given point. + */ + public static double getLongitude(Point location) { + return location.getLongitude() / COORD_FACTOR; + } + + /** + * Gets the default features file from classpath. + */ + public static URL getDefaultFeaturesFile() { + return RouteGuideServer.class.getResource("route_guide_db.json"); + } + + /** + * Parses the JSON input file containing the list of features. + */ + public static List parseFeatures(URL file) throws IOException { + InputStream input = file.openStream(); + try { + JsonReader reader = Json.createReader(input); + List features = new ArrayList(); + for (JsonValue value : reader.readArray()) { + JsonObject obj = (JsonObject) value; + String name = obj.getString("name", ""); + JsonObject location = obj.getJsonObject("location"); + int lat = location.getInt("latitude"); + int lon = location.getInt("longitude"); + Feature feature = + Feature + .newBuilder() + .setName(name) + .setLocation( + Point.newBuilder().setLatitude(lat) + .setLongitude(lon).build()).build(); + features.add(feature); + } + + return features; + } finally { + input.close(); + } + } + + /** + * Indicates whether the given feature exists (i.e. has a valid name). + */ + public static boolean exists(Feature feature) { + return feature != null && !feature.getName().isEmpty(); + } +} diff --git a/examples/src/main/java/io/grpc/examples/StockClient.java b/examples/src/main/java/io/grpc/examples/StockClient.java deleted file mode 100644 index 1daa87b9de..0000000000 --- a/examples/src/main/java/io/grpc/examples/StockClient.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2014, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package io.grpc.examples; - -import io.grpc.examples.StockGrpc; -import io.grpc.examples.StockGrpc.StockBlockingStub; -import io.grpc.examples.StockGrpc.StockStub; -import io.grpc.examples.StockOuterClass.StockReply; -import io.grpc.examples.StockOuterClass.StockRequest; - -import io.grpc.ChannelImpl; -import io.grpc.stub.StreamObserver; -import io.grpc.transport.netty.NegotiationType; -import io.grpc.transport.netty.NettyChannelBuilder; - -import java.util.Iterator; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Sample client code that makes GRPC calls to the server. - */ -public class StockClient { - - private final ChannelImpl channel = NettyChannelBuilder.forAddress("localhost", 8980) - .negotiationType(NegotiationType.PLAINTEXT) - .build(); - - public void shutdown() throws InterruptedException { - channel.shutdown().awaitTerminated(5, TimeUnit.SECONDS); - } - - public void makeBlockingSimpleCall() { - StockBlockingStub stub = StockGrpc.newBlockingStub(channel) - .configureNewStub().setTimeout(2, TimeUnit.SECONDS).build(); - StockRequest request = StockRequest.newBuilder().setSymbol("GOOG").build(); - System.out.println("***Blocking simple call, request=" + request); - StockReply reply = stub.getLastTradePrice(request); - System.out.println("response=" + reply); - } - - public void makeAsyncSimpleCall() { - StockStub stub = StockGrpc.newStub(channel) - .configureNewStub().setTimeout(2, TimeUnit.SECONDS).build(); - StockRequest request = StockRequest.newBuilder().setSymbol("MSFT").build(); - System.out.println("***Async simple call, request=" + request); - stub.getLastTradePrice(request, new StreamObserver() { - StockReply response; - @Override - public void onValue(StockReply response) { - this.response = response; - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - } - - @Override - public void onCompleted() { - System.out.println("Completed, response=" + response); - } - }); - } - - public void makeSequentialCalls() { - StockBlockingStub stub = StockGrpc.newBlockingStub(channel) - .configureNewStub().setTimeout(2, TimeUnit.SECONDS).build(); - System.out.println("***Making sequential calls"); - StockRequest request1 = StockRequest.newBuilder().setSymbol("AMZN").build(); - System.out.println("First request=" + request1); - StockReply response1 = stub.getLastTradePrice(request1); - System.out.println("First response=" + response1); - StockRequest request2 = StockRequest.newBuilder().setSymbol(response1.getSymbol()).build(); - System.out.println("Second request=" + request2); - StockReply response2 = stub.getLastTradePrice(request2); - System.out.println("Second response=" + response2); - } - - public void makeAsyncCalls() throws InterruptedException { - StockStub stub = StockGrpc.newStub(channel) - .configureNewStub().setTimeout(2, TimeUnit.SECONDS).build(); - System.out.println("***Making two calls in parallel"); - final StockRequest request1 = StockRequest.newBuilder().setSymbol("IBM").build(); - final StockRequest request2 = StockRequest.newBuilder().setSymbol("APPL").build(); - final CountDownLatch completeLatch = new CountDownLatch(2); - stub.getLastTradePrice(request1, new StreamObserver() { - StockReply response; - @Override - public void onValue(StockReply response) { - this.response = response; - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - } - - @Override - public void onCompleted() { - System.out.println("Completed for first request=" + request1 + ", response=" + response); - completeLatch.countDown(); - } - }); - stub.getLastTradePrice(request2, new StreamObserver() { - StockReply response; - @Override - public void onValue(StockReply response) { - this.response = response; - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - } - - @Override - public void onCompleted() { - System.out.println("Completed for second request=" + request2 + ", response=" + response); - completeLatch.countDown(); - } - }); - completeLatch.await(); - } - - public void makeServerStreamingCall() { - StockBlockingStub stub = StockGrpc.newBlockingStub(channel) - .configureNewStub().setTimeout(2, TimeUnit.SECONDS).build(); - StockRequest request = StockRequest.newBuilder().setSymbol("FB").setNumTradesToWatch(5).build(); - System.out.println("***Making a server streaming call, request=" + request); - for (Iterator responses = stub.watchFutureTrades(request); responses.hasNext(); ) { - StockReply response = responses.next(); - System.out.println("Response=" + response); - } - System.out.println("Completed"); - } - - public void makeClientStreamingCall() throws InterruptedException { - StockStub stub = StockGrpc.newStub(channel) - .configureNewStub().setTimeout(2, TimeUnit.SECONDS).build(); - System.out.println("***Making a client streaming call"); - final CountDownLatch completeLatch = new CountDownLatch(1); - StreamObserver requestSink = stub.getHighestTradePrice( - new StreamObserver() { - StockReply response; - @Override - public void onValue(StockReply response) { - this.response = response; - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - } - - @Override - public void onCompleted() { - System.out.println("Completed. response=" + response); - completeLatch.countDown(); - } - }); - for (String symbol : new String[] {"ORCL", "TWTR", "QQQ", "SIRI", "ZNGA", "RAD"}) { - StockRequest request = StockRequest.newBuilder().setSymbol(symbol).build(); - System.out.println("request=" + request); - requestSink.onValue(request); - } - requestSink.onCompleted(); - completeLatch.await(); - } - - public static void main(String[] args) throws InterruptedException { - StockClient client = new StockClient(); - try { - client.makeBlockingSimpleCall(); - client.makeAsyncSimpleCall(); - client.makeSequentialCalls(); - client.makeAsyncCalls(); - client.makeServerStreamingCall(); - client.makeClientStreamingCall(); - System.out.println("***All done"); - } finally { - client.shutdown(); - } - } -} diff --git a/examples/src/main/java/io/grpc/examples/StockServer.java b/examples/src/main/java/io/grpc/examples/StockServer.java deleted file mode 100644 index a619006fd1..0000000000 --- a/examples/src/main/java/io/grpc/examples/StockServer.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2014, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package io.grpc.examples; - -import io.grpc.examples.StockGrpc; -import io.grpc.examples.StockOuterClass.StockReply; -import io.grpc.examples.StockOuterClass.StockRequest; - -import io.grpc.ServerImpl; -import io.grpc.stub.StreamObserver; -import io.grpc.transport.netty.NettyServerBuilder; - -import java.util.Random; - -/** - * A sample GRPC server that implements the Stock service. - */ -public class StockServer implements StockGrpc.Stock { - - private final Random rand = new Random(); - - private float getStockPrice(String symbol) { - return rand.nextFloat() * 1000; - } - - // Simple request-response RPC - @Override - public void getLastTradePrice(StockRequest request, StreamObserver responseObserver) { - String symbol = request.getSymbol(); - float price = getStockPrice(symbol); - responseObserver.onValue( - StockReply.newBuilder().setSymbol(symbol).setPrice(price).build()); - responseObserver.onCompleted(); - } - - // Bi-directional streaming - @Override - public StreamObserver getLastTradePriceMultiple( - final StreamObserver responseObserver) { - return new StreamObserver() { - @Override - public void onValue(StockRequest request) { - String symbol = request.getSymbol(); - float price = getStockPrice(symbol); - responseObserver.onValue( - StockReply.newBuilder().setSymbol(symbol).setPrice(price).build()); - } - - @Override - public void onError(Throwable t) { - } - - @Override - public void onCompleted() { - responseObserver.onCompleted(); - } - }; - } - - // Server streaming - @Override - public void watchFutureTrades(StockRequest request, StreamObserver responseObserver) { - String symbol = request.getSymbol(); - for (int i = 0; i < request.getNumTradesToWatch(); i++) { - float price = getStockPrice(symbol); - responseObserver.onValue( - StockReply.newBuilder().setSymbol(symbol).setPrice(price).build()); - } - responseObserver.onCompleted(); - } - - // Client streaming - @Override - public StreamObserver getHighestTradePrice( - final StreamObserver responseObserver) { - return new StreamObserver() { - String highestSymbol; - float highestPrice; - - @Override - public void onValue(StockRequest request) { - String symbol = request.getSymbol(); - float price = getStockPrice(symbol); - if (highestSymbol == null || price > highestPrice) { - highestPrice = price; - highestSymbol = symbol; - } - } - - @Override - public void onError(Throwable t) { - } - - @Override - public void onCompleted() { - if (highestSymbol != null) { - responseObserver.onValue( - StockReply.newBuilder().setSymbol(highestSymbol).setPrice(highestPrice).build()); - } - responseObserver.onCompleted(); - } - }; - } - - public static void main(String[] args) throws Exception { - ServerImpl server = NettyServerBuilder.forPort(8980) - .addService(StockGrpc.bindService(new StockServer())) - .build().start(); - System.out.println("Server started"); - } -} diff --git a/examples/src/main/proto/math.proto b/examples/src/main/proto/math.proto deleted file mode 100644 index d1509fbc69..0000000000 --- a/examples/src/main/proto/math.proto +++ /dev/null @@ -1,52 +0,0 @@ -syntax = "proto2"; - -package io.grpc.examples; -option java_package = "io.grpc.examples"; -option java_outer_classname = "Math"; - -message DivArgs { - required int64 dividend = 1; - required int64 divisor = 2; -} - -message DivReply { - required int64 quotient = 1; - required int64 remainder = 2; -} - -message FibArgs { - optional int64 limit = 1; -} - -message Num { - required int64 num = 1; -} - -message FibReply { - required int64 count = 1; -} - -service Calc { - // Div divides args.dividend by args.divisor and returns the quotient and - // remainder. - rpc Div (DivArgs) returns (DivReply) { - } - - // DivMany accepts an arbitrary number of division args from the client stream - // and sends back the results in the reply stream. The stream continues until - // the client closes its end; the server does the same after sending all the - // replies. The stream ends immediately if either end aborts. - rpc DivMany (stream DivArgs) returns (stream DivReply) { - } - - // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib - // generates up to limit numbers; otherwise it continues until the call is - // canceled. Unlike Fib above, Fib has no final FibReply. - rpc Fib (FibArgs) returns (stream Num) { - } - - // Sum sums a stream of numbers, returning the final result once the stream - // is closed. - rpc Sum (stream Num) returns (Num) { - } -} \ No newline at end of file diff --git a/examples/src/main/proto/route_guide.proto b/examples/src/main/proto/route_guide.proto new file mode 100644 index 0000000000..8c975d72d0 --- /dev/null +++ b/examples/src/main/proto/route_guide.proto @@ -0,0 +1,120 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +option java_package = "io.grpc.examples"; + +package examples; + +// Interface exported by the server. +service RouteGuide { + // A simple RPC. + // + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} + + // A server-to-client streaming RPC. + // + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + // A client-to-server streaming RPC. + // + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + // A Bidirectional streaming RPC. + // + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +} + +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + optional int32 latitude = 1; + optional int32 longitude = 2; +} + +// A latitude-longitude rectangle, represented as two diagonally opposite +// points "lo" and "hi". +message Rectangle { + // One corner of the rectangle. + optional Point lo = 1; + + // The other corner of the rectangle. + optional Point hi = 2; +} + +// A feature names something at a given point. +// +// If a feature could not be named, the name is empty. +message Feature { + // The name of the feature. + optional string name = 1; + + // The point where the feature is detected. + optional Point location = 2; +} + +// A RouteNote is a message sent while at a given point. +message RouteNote { + // The location from which the message is sent. + optional Point location = 1; + + // The message to be sent. + optional string message = 2; +} + +// A RouteSummary is received in response to a RecordRoute rpc. +// +// It contains the number of individual points received, the number of +// detected features, and the total distance covered as the cumulative sum of +// the distance between each point. +message RouteSummary { + // The number of points received. + optional int32 point_count = 1; + + // The number of known features passed while traversing the route. + optional int32 feature_count = 2; + + // The distance covered in metres. + optional int32 distance = 3; + + // The duration of the traversal in seconds. + optional int32 elapsed_time = 4; +} \ No newline at end of file diff --git a/examples/src/main/proto/stock.proto b/examples/src/main/proto/stock.proto deleted file mode 100644 index 09c4502dc1..0000000000 --- a/examples/src/main/proto/stock.proto +++ /dev/null @@ -1,34 +0,0 @@ -syntax = "proto2"; - -package io.grpc.examples; -option java_package = "io.grpc.examples"; - -// Protocol type definitions -message StockRequest { - optional string symbol = 1; - optional int32 num_trades_to_watch = 2 [default=0]; -}; - -message StockReply { - optional float price = 1; - optional string symbol = 2; -}; - - -// Interface exported by the server -service Stock { - // Simple blocking RPC - rpc GetLastTradePrice(StockRequest) returns (StockReply) { - }; - // Bidirectional streaming RPC - rpc GetLastTradePriceMultiple(stream StockRequest) returns - (stream StockReply) { - }; - // Unidirectional server-to-client streaming RPC - rpc WatchFutureTrades(StockRequest) returns (stream StockReply) { - }; - // Unidirectional client-to-server streaming RPC - rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) { - }; - -}; diff --git a/examples/src/main/resources/io/grpc/examples/route_guide_db.json b/examples/src/main/resources/io/grpc/examples/route_guide_db.json new file mode 100644 index 0000000000..9342beb579 --- /dev/null +++ b/examples/src/main/resources/io/grpc/examples/route_guide_db.json @@ -0,0 +1,601 @@ +[{ + "location": { + "latitude": 407838351, + "longitude": -746143763 + }, + "name": "Patriots Path, Mendham, NJ 07945, USA" +}, { + "location": { + "latitude": 408122808, + "longitude": -743999179 + }, + "name": "101 New Jersey 10, Whippany, NJ 07981, USA" +}, { + "location": { + "latitude": 413628156, + "longitude": -749015468 + }, + "name": "U.S. 6, Shohola, PA 18458, USA" +}, { + "location": { + "latitude": 419999544, + "longitude": -740371136 + }, + "name": "5 Conners Road, Kingston, NY 12401, USA" +}, { + "location": { + "latitude": 414008389, + "longitude": -743951297 + }, + "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA" +}, { + "location": { + "latitude": 419611318, + "longitude": -746524769 + }, + "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA" +}, { + "location": { + "latitude": 406109563, + "longitude": -742186778 + }, + "name": "4001 Tremley Point Road, Linden, NJ 07036, USA" +}, { + "location": { + "latitude": 416802456, + "longitude": -742370183 + }, + "name": "352 South Mountain Road, Wallkill, NY 12589, USA" +}, { + "location": { + "latitude": 412950425, + "longitude": -741077389 + }, + "name": "Bailey Turn Road, Harriman, NY 10926, USA" +}, { + "location": { + "latitude": 412144655, + "longitude": -743949739 + }, + "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA" +}, { + "location": { + "latitude": 415736605, + "longitude": -742847522 + }, + "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA" +}, { + "location": { + "latitude": 413843930, + "longitude": -740501726 + }, + "name": "162 Merrill Road, Highland Mills, NY 10930, USA" +}, { + "location": { + "latitude": 410873075, + "longitude": -744459023 + }, + "name": "Clinton Road, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 412346009, + "longitude": -744026814 + }, + "name": "16 Old Brook Lane, Warwick, NY 10990, USA" +}, { + "location": { + "latitude": 402948455, + "longitude": -747903913 + }, + "name": "3 Drake Lane, Pennington, NJ 08534, USA" +}, { + "location": { + "latitude": 406337092, + "longitude": -740122226 + }, + "name": "6324 8th Avenue, Brooklyn, NY 11220, USA" +}, { + "location": { + "latitude": 406421967, + "longitude": -747727624 + }, + "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA" +}, { + "location": { + "latitude": 416318082, + "longitude": -749677716 + }, + "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA" +}, { + "location": { + "latitude": 415301720, + "longitude": -748416257 + }, + "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA" +}, { + "location": { + "latitude": 402647019, + "longitude": -747071791 + }, + "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA" +}, { + "location": { + "latitude": 412567807, + "longitude": -741058078 + }, + "name": "New York State Reference Route 987E, Southfields, NY 10975, USA" +}, { + "location": { + "latitude": 416855156, + "longitude": -744420597 + }, + "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA" +}, { + "location": { + "latitude": 404663628, + "longitude": -744820157 + }, + "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA" +}, { + "location": { + "latitude": 407113723, + "longitude": -749746483 + }, + "name": "" +}, { + "location": { + "latitude": 402133926, + "longitude": -743613249 + }, + "name": "" +}, { + "location": { + "latitude": 400273442, + "longitude": -741220915 + }, + "name": "" +}, { + "location": { + "latitude": 411236786, + "longitude": -744070769 + }, + "name": "" +}, { + "location": { + "latitude": 411633782, + "longitude": -746784970 + }, + "name": "211-225 Plains Road, Augusta, NJ 07822, USA" +}, { + "location": { + "latitude": 415830701, + "longitude": -742952812 + }, + "name": "" +}, { + "location": { + "latitude": 413447164, + "longitude": -748712898 + }, + "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA" +}, { + "location": { + "latitude": 405047245, + "longitude": -749800722 + }, + "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA" +}, { + "location": { + "latitude": 418858923, + "longitude": -746156790 + }, + "name": "" +}, { + "location": { + "latitude": 417951888, + "longitude": -748484944 + }, + "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA" +}, { + "location": { + "latitude": 407033786, + "longitude": -743977337 + }, + "name": "26 East 3rd Street, New Providence, NJ 07974, USA" +}, { + "location": { + "latitude": 417548014, + "longitude": -740075041 + }, + "name": "" +}, { + "location": { + "latitude": 410395868, + "longitude": -744972325 + }, + "name": "" +}, { + "location": { + "latitude": 404615353, + "longitude": -745129803 + }, + "name": "" +}, { + "location": { + "latitude": 406589790, + "longitude": -743560121 + }, + "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA" +}, { + "location": { + "latitude": 414653148, + "longitude": -740477477 + }, + "name": "18 Lannis Avenue, New Windsor, NY 12553, USA" +}, { + "location": { + "latitude": 405957808, + "longitude": -743255336 + }, + "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA" +}, { + "location": { + "latitude": 411733589, + "longitude": -741648093 + }, + "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA" +}, { + "location": { + "latitude": 412676291, + "longitude": -742606606 + }, + "name": "1270 Lakes Road, Monroe, NY 10950, USA" +}, { + "location": { + "latitude": 409224445, + "longitude": -748286738 + }, + "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA" +}, { + "location": { + "latitude": 406523420, + "longitude": -742135517 + }, + "name": "652 Garden Street, Elizabeth, NJ 07202, USA" +}, { + "location": { + "latitude": 401827388, + "longitude": -740294537 + }, + "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA" +}, { + "location": { + "latitude": 410564152, + "longitude": -743685054 + }, + "name": "13-17 Stanley Street, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 408472324, + "longitude": -740726046 + }, + "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA" +}, { + "location": { + "latitude": 412452168, + "longitude": -740214052 + }, + "name": "5 White Oak Lane, Stony Point, NY 10980, USA" +}, { + "location": { + "latitude": 409146138, + "longitude": -746188906 + }, + "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" +}, { + "location": { + "latitude": 404701380, + "longitude": -744781745 + }, + "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 409642566, + "longitude": -746017679 + }, + "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA" +}, { + "location": { + "latitude": 408031728, + "longitude": -748645385 + }, + "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA" +}, { + "location": { + "latitude": 413700272, + "longitude": -742135189 + }, + "name": "367 Prospect Road, Chester, NY 10918, USA" +}, { + "location": { + "latitude": 404310607, + "longitude": -740282632 + }, + "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA" +}, { + "location": { + "latitude": 409319800, + "longitude": -746201391 + }, + "name": "11 Ward Street, Mount Arlington, NJ 07856, USA" +}, { + "location": { + "latitude": 406685311, + "longitude": -742108603 + }, + "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA" +}, { + "location": { + "latitude": 419018117, + "longitude": -749142781 + }, + "name": "43 Dreher Road, Roscoe, NY 12776, USA" +}, { + "location": { + "latitude": 412856162, + "longitude": -745148837 + }, + "name": "Swan Street, Pine Island, NY 10969, USA" +}, { + "location": { + "latitude": 416560744, + "longitude": -746721964 + }, + "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA" +}, { + "location": { + "latitude": 405314270, + "longitude": -749836354 + }, + "name": "" +}, { + "location": { + "latitude": 414219548, + "longitude": -743327440 + }, + "name": "" +}, { + "location": { + "latitude": 415534177, + "longitude": -742900616 + }, + "name": "565 Winding Hills Road, Montgomery, NY 12549, USA" +}, { + "location": { + "latitude": 406898530, + "longitude": -749127080 + }, + "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA" +}, { + "location": { + "latitude": 407586880, + "longitude": -741670168 + }, + "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA" +}, { + "location": { + "latitude": 400106455, + "longitude": -742870190 + }, + "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA" +}, { + "location": { + "latitude": 400066188, + "longitude": -746793294 + }, + "name": "" +}, { + "location": { + "latitude": 418803880, + "longitude": -744102673 + }, + "name": "40 Mountain Road, Napanoch, NY 12458, USA" +}, { + "location": { + "latitude": 414204288, + "longitude": -747895140 + }, + "name": "" +}, { + "location": { + "latitude": 414777405, + "longitude": -740615601 + }, + "name": "" +}, { + "location": { + "latitude": 415464475, + "longitude": -747175374 + }, + "name": "48 North Road, Forestburgh, NY 12777, USA" +}, { + "location": { + "latitude": 404062378, + "longitude": -746376177 + }, + "name": "" +}, { + "location": { + "latitude": 405688272, + "longitude": -749285130 + }, + "name": "" +}, { + "location": { + "latitude": 400342070, + "longitude": -748788996 + }, + "name": "" +}, { + "location": { + "latitude": 401809022, + "longitude": -744157964 + }, + "name": "" +}, { + "location": { + "latitude": 404226644, + "longitude": -740517141 + }, + "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA" +}, { + "location": { + "latitude": 410322033, + "longitude": -747871659 + }, + "name": "" +}, { + "location": { + "latitude": 407100674, + "longitude": -747742727 + }, + "name": "" +}, { + "location": { + "latitude": 418811433, + "longitude": -741718005 + }, + "name": "213 Bush Road, Stone Ridge, NY 12484, USA" +}, { + "location": { + "latitude": 415034302, + "longitude": -743850945 + }, + "name": "" +}, { + "location": { + "latitude": 411349992, + "longitude": -743694161 + }, + "name": "" +}, { + "location": { + "latitude": 404839914, + "longitude": -744759616 + }, + "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 414638017, + "longitude": -745957854 + }, + "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA" +}, { + "location": { + "latitude": 412127800, + "longitude": -740173578 + }, + "name": "" +}, { + "location": { + "latitude": 401263460, + "longitude": -747964303 + }, + "name": "" +}, { + "location": { + "latitude": 412843391, + "longitude": -749086026 + }, + "name": "" +}, { + "location": { + "latitude": 418512773, + "longitude": -743067823 + }, + "name": "" +}, { + "location": { + "latitude": 404318328, + "longitude": -740835638 + }, + "name": "42-102 Main Street, Belford, NJ 07718, USA" +}, { + "location": { + "latitude": 419020746, + "longitude": -741172328 + }, + "name": "" +}, { + "location": { + "latitude": 404080723, + "longitude": -746119569 + }, + "name": "" +}, { + "location": { + "latitude": 401012643, + "longitude": -744035134 + }, + "name": "" +}, { + "location": { + "latitude": 404306372, + "longitude": -741079661 + }, + "name": "" +}, { + "location": { + "latitude": 403966326, + "longitude": -748519297 + }, + "name": "" +}, { + "location": { + "latitude": 405002031, + "longitude": -748407866 + }, + "name": "" +}, { + "location": { + "latitude": 409532885, + "longitude": -742200683 + }, + "name": "" +}, { + "location": { + "latitude": 416851321, + "longitude": -742674555 + }, + "name": "" +}, { + "location": { + "latitude": 406411633, + "longitude": -741722051 + }, + "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA" +}, { + "location": { + "latitude": 413069058, + "longitude": -744597778 + }, + "name": "261 Van Sickle Road, Goshen, NY 10924, USA" +}, { + "location": { + "latitude": 418465462, + "longitude": -746859398 + }, + "name": "" +}, { + "location": { + "latitude": 411733222, + "longitude": -744228360 + }, + "name": "" +}, { + "location": { + "latitude": 410248224, + "longitude": -747127767 + }, + "name": "3 Hasta Way, Newton, NJ 07860, USA" +}] \ No newline at end of file