aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorDino Oliva <dpo@google.com>2016-05-20 16:39:02 -0700
committerDino Oliva <dpo@google.com>2016-05-20 16:39:02 -0700
commit6712e0f623f659ee497b85be255671b961c2f647 (patch)
tree5a5d8f72839645e8275135c0e76dd1fbbfaccdd3 /examples
downloadplatform_external_opencensus-java-6712e0f623f659ee497b85be255671b961c2f647.tar.gz
platform_external_opencensus-java-6712e0f623f659ee497b85be255671b961c2f647.tar.bz2
platform_external_opencensus-java-6712e0f623f659ee497b85be255671b961c2f647.zip
Initial import of Java Census.
Diffstat (limited to 'examples')
-rw-r--r--examples/java/com/google/census/examples/BUILD20
-rw-r--r--examples/java/com/google/census/examples/gol.proto55
-rw-r--r--examples/java/com/google/census/examples/gol/BUILD45
-rw-r--r--examples/java/com/google/census/examples/gol/CensusApplication.java160
-rw-r--r--examples/java/com/google/census/examples/gol/CensusClient.java126
-rw-r--r--examples/java/com/google/census/examples/gol/CensusClientz.java208
-rw-r--r--examples/java/com/google/census/examples/gol/CensusServer.java168
-rw-r--r--examples/java/com/google/census/examples/gol/GameOfLife.java176
-rw-r--r--examples/java/com/google/census/examples/gol/census_gol.borg51
9 files changed, 1009 insertions, 0 deletions
diff --git a/examples/java/com/google/census/examples/BUILD b/examples/java/com/google/census/examples/BUILD
new file mode 100644
index 00000000..d2988d3d
--- /dev/null
+++ b/examples/java/com/google/census/examples/BUILD
@@ -0,0 +1,20 @@
+load("//third_party/java/grpc:build_defs.bzl", "grpc_java_library")
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # BSD
+
+exports_files(["LICENSE"])
+
+proto_library(
+ name = "gol_proto",
+ srcs = ["gol.proto"],
+ has_services = 1,
+ java_api_version = 2,
+)
+
+grpc_java_library(
+ name = "gol_proto_grpc",
+ src = "gol.proto",
+ proto_deps = [":gol_proto"],
+)
diff --git a/examples/java/com/google/census/examples/gol.proto b/examples/java/com/google/census/examples/gol.proto
new file mode 100644
index 00000000..d861926a
--- /dev/null
+++ b/examples/java/com/google/census/examples/gol.proto
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+// The definition of the example census service.
+
+syntax = "proto3";
+
+package monitoring.census;
+option java_multiple_files = true;
+option java_package = "com.google.census.examples";
+option java_outer_classname = "GolProto";
+
+// The example service definition.
+service CommandProcessor {
+ // Executes the command.
+ rpc Execute(CommandRequest) returns (CommandResponse) { }
+}
+
+// The request message containing the user's command.
+message CommandRequest {
+ string req = 1;
+}
+
+// The response message containing the user's command result.
+message CommandResponse {
+ string retval = 1;
+}
diff --git a/examples/java/com/google/census/examples/gol/BUILD b/examples/java/com/google/census/examples/gol/BUILD
new file mode 100644
index 00000000..2228f0a1
--- /dev/null
+++ b/examples/java/com/google/census/examples/gol/BUILD
@@ -0,0 +1,45 @@
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # BSD
+
+exports_files(["LICENSE"])
+
+java_binary(
+ name = "CensusClient",
+ srcs = [
+ "CensusApplication.java",
+ "CensusClient.java",
+ "CensusClientz.java",
+ ],
+ main_class = "com.google.census.examples.gol.CensusClient",
+ deps = [
+ "//java/com/google/common/base",
+ "//java/com/google/common/flags",
+ "//java/com/google/monitoring/runtime:hooks",
+ "//third_party/java/grpc:core",
+ "//third_party/java_src/census",
+ "//third_party/java_src/census:census_google3",
+ "//third_party/java_src/census:census_grpc",
+ "//third_party/java_src/census/examples/java/com/google/census/examples:gol_proto",
+ "//third_party/java_src/census/examples/java/com/google/census/examples:gol_proto_grpc",
+ ],
+)
+
+java_binary(
+ name = "CensusServer",
+ srcs = [
+ "CensusServer.java",
+ "GameOfLife.java",
+ ],
+ main_class = "com.google.census.examples.gol.CensusServer",
+ deps = [
+ "//java/com/google/common/flags",
+ "//third_party/java/grpc:core",
+ "//third_party/java/grpc:stub",
+ "//third_party/java_src/census",
+ "//third_party/java_src/census:census_google3",
+ "//third_party/java_src/census:census_grpc",
+ "//third_party/java_src/census/examples/java/com/google/census/examples:gol_proto",
+ "//third_party/java_src/census/examples/java/com/google/census/examples:gol_proto_grpc",
+ ],
+)
diff --git a/examples/java/com/google/census/examples/gol/CensusApplication.java b/examples/java/com/google/census/examples/gol/CensusApplication.java
new file mode 100644
index 00000000..d54f72dc
--- /dev/null
+++ b/examples/java/com/google/census/examples/gol/CensusApplication.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2016, 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 com.google.census.examples.gol;
+
+import com.google.census.CensusScope;
+import com.google.census.TagKey;
+import com.google.census.TagMap;
+
+// Invokes the given CensusClient for all of the Game-of-Life specs with the appropriate
+// Census client tag in scope.
+class CensusApplication {
+ static final TagKey CLIENT_KEY = new TagKey("census_gol_client");
+
+ final CensusClient client;
+ final int gensPerRpc;
+ final int numRpcs;
+
+ CensusApplication(CensusClient client, int gensPerRpc, int numRpcs) {
+ this.client = client;
+ this.gensPerRpc = gensPerRpc;
+ this.numRpcs = numRpcs;
+ }
+
+ void execute() {
+ for (GolSpec gol : gols) {
+ TagMap tags = TagMap.of(CLIENT_KEY, getTagValue(gol.dim, gensPerRpc));
+ String request = getRequest(gol.currentGen, gensPerRpc);
+ try (CensusScope scope = new CensusScope(tags)) {
+ for (int i = 0; i < numRpcs; ++i) {
+ String result = client.executeCommand(request);
+ String[] results = result.split("; ");
+ if (results.length < 1) {
+ break;
+ }
+ gol.currentGen = results[0];
+ gol.gens += gensPerRpc;
+ }
+ }
+ }
+ }
+
+ // Generates tag value as <dimension>x<dimension>-<generations>.
+ static String getTagValue(int dim, int gensPerRpc) {
+ return dim + "x" + dim + "-" + gensPerRpc;
+ }
+
+ // Encodes a request for the given generation and number of generations to calculate.
+ static String getRequest(String gen, int gensPerRpc) {
+ return "gol " + gensPerRpc + " " + gen;
+ }
+
+ // Holds the specification of an RPC for the game of life server.
+ static class GolSpec {
+ final int dim;
+ final String initGen;
+ String currentGen;
+ long gens;
+
+ GolSpec(String initGen) {
+ this.initGen = initGen;
+ this.dim = (int) Math.sqrt(initGen.length());
+ this.currentGen = initGen;
+ this.gens = 0;
+ }
+ }
+
+ static final GolSpec[] gols = new GolSpec[] {
+
+ new GolSpec(""
+ + "00000000"
+ + "00111000"
+ + "00000000"
+ + "00000000"
+ + "00010000"
+ + "00010000"
+ + "00010000"
+ + "00000000"),
+
+ new GolSpec(""
+ + "0000000000000000"
+ + "0011100000111000"
+ + "0000000000000000"
+ + "0000000000000000"
+ + "0001000000010000"
+ + "0001000000010000"
+ + "0001000000010000"
+ + "0000000000000000"
+ + "0000000000000000"
+ + "0011100000111000"
+ + "0000000000000000"
+ + "0000000000000000"
+ + "0001000000010000"
+ + "0001000000010000"
+ + "0001000000010000"
+ + "0000000000000000"),
+
+ new GolSpec(""
+ + "00000000000000000000000000000000"
+ + "00111000001110000011100000111000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00010000000100000001000000010000"
+ + "00010000000100000001000000010000"
+ + "00010000000100000001000000010000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00111000001110000011100000111000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00010000000100000001000000010000"
+ + "00010000000100000001000000010000"
+ + "00010000000100000001000000010000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00111000001110000011100000111000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00010000000100000001000000010000"
+ + "00010000000100000001000000010000"
+ + "00010000000100000001000000010000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00111000001110000011100000111000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00010000000100000001000000010000"
+ + "00010000000100000001000000010000"
+ + "00010000000100000001000000010000"
+ + "00000000000000000000000000000000"),
+ };
+}
diff --git a/examples/java/com/google/census/examples/gol/CensusClient.java b/examples/java/com/google/census/examples/gol/CensusClient.java
new file mode 100644
index 00000000..34981cd1
--- /dev/null
+++ b/examples/java/com/google/census/examples/gol/CensusClient.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016, 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 com.google.census.examples.gol;
+
+import com.google.census.CensusClientInterceptor;
+import com.google.census.examples.CommandProcessorGrpc;
+import com.google.census.examples.CommandRequest;
+import com.google.census.examples.CommandResponse;
+import com.google.common.flags.Flag;
+import com.google.common.flags.FlagSpec;
+import com.google.common.flags.Flags;
+import com.google.monitoring.runtime.HookManager;
+import com.google.net.rpc3.server.RpcServer;
+
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * A client for the Census Game-of-Life example.
+ */
+public class CensusClient {
+ private static final Logger logger = Logger.getLogger(CensusClient.class.getName());
+
+ private final ManagedChannel channel;
+ private final CommandProcessorGrpc.CommandProcessorBlockingStub blockingStub;
+
+ @FlagSpec(
+ name = "port",
+ help = "The Game-of-Life client stubby port.")
+ private static final Flag<Integer> port = Flag.value(10001);
+
+ @FlagSpec(
+ name = "gol_server_grpc_port",
+ help = "The Game-of-Life server gRPC port.")
+ private static final Flag<Integer> golServerGrpcPort = Flag.value(20000);
+
+ @FlagSpec(
+ name = "gol_server_host",
+ help = "The Game-of-Life server host.")
+ private static final Flag<String> golServerHost = Flag.value("localhost");
+
+ @FlagSpec(
+ name = "gol_gens_per_rpc",
+ help = "The number of generations to calculate per rpc.")
+ private static final Flag<Integer> golGensPerRpc = Flag.value(1001);
+
+ /** Construct client connecting to GameOfLife server at {@code host:port}. */
+ public CensusClient(String host, int port) {
+ channel = ManagedChannelBuilder.forAddress(host, port)
+ .usePlaintext(true)
+ .intercept(new CensusClientInterceptor())
+ .build();
+ blockingStub = CommandProcessorGrpc.newBlockingStub(channel);
+ }
+
+ public void shutdown() throws InterruptedException {
+ channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Invokes the synchronous RPC call that remotely executes the census command.
+ * Returns the result of executing the command.
+ *
+ * @param command the command string to send
+ * @return the result of executing the command
+ */
+ public String executeCommand(String command) {
+ try {
+ CommandRequest request = CommandRequest.newBuilder().setReq(command).build();
+ CommandResponse response = blockingStub.execute(request);
+ return response.getRetval();
+ } catch (RuntimeException e) {
+ logger.log(Level.WARNING, "RPC failed", e);
+ return "";
+ }
+ }
+
+ /** Game of Life client. */
+ public static void main(String[] args) throws Exception {
+ // TODO(dpo): this call is necessary to run the completion hook to enable CensusNative. In
+ // general, G3 services built with gRPC will need to do this otherwise many libries will not
+ // work correctly. This issue needs to be resolved with the gRPC team.
+ Flags.parse(args);
+
+ HookManager.getManager().addHook(
+ new CensusClientz(golServerHost.get(), golServerGrpcPort.get(), golGensPerRpc.get()));
+
+ // TODO(dpo): remove stubby-based server for monitoring hooks once gRPC team has implemented
+ // direct support.
+ RpcServer.newBuilder(port.get()).createAndStart().blockUntilShutdown();
+ }
+}
diff --git a/examples/java/com/google/census/examples/gol/CensusClientz.java b/examples/java/com/google/census/examples/gol/CensusClientz.java
new file mode 100644
index 00000000..7da1fc10
--- /dev/null
+++ b/examples/java/com/google/census/examples/gol/CensusClientz.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2016, 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 com.google.census.examples.gol;
+
+import com.google.monitoring.runtime.MonitoringHook;
+import com.google.monitoring.runtime.RequestHandlerAdapter;
+
+import java.io.PrintWriter;
+import java.util.Map;
+
+/**
+ * Censusz handler. Displays Census internal state.
+ *
+ * @author dpo@google.com (Dino Oliva)
+ */
+public class CensusClientz implements MonitoringHook {
+ private final String defaultHost;
+ private final int defaultPort;
+ private final int gensPerRpc;
+
+ public CensusClientz(String host, int defaultPort, int gensPerRpc) {
+ this.defaultHost = host;
+ this.defaultPort = defaultPort;
+ this.gensPerRpc = gensPerRpc;
+ }
+
+ @Override
+ public String getLinkName() {
+ return "censusclientz";
+ }
+
+ @Override
+ public String getLinkHtml(String linkUrl) {
+ return "Show <a href=\"" + linkUrl + "\">censusclientz</a>.";
+ }
+
+ @Override
+ public void handleRequest(RequestHandlerAdapter handler) throws Exception {
+ // set params based on input
+ Map<String, String[]> params = handler.getParameterMap();
+ String host = getParamValueOrDefault(params, "server", defaultHost);
+ int port = getIntOrDefault(getParamValue(params, "port"), defaultPort);
+ int numRpcs = getIntOrDefault(getParamValue(params, "rpcs"), 0);
+ CensusClient client = new CensusClient(host, port);
+ try {
+ // execute rpcs
+ new CensusApplication(client, gensPerRpc, numRpcs).execute();
+ // display result
+ handler.setContentType("text/html");
+ PrintWriter pw = handler.getPrintWriter();
+ writePage(pw, host, port, numRpcs);
+ return;
+ } finally {
+ client.shutdown();
+ }
+ }
+
+ private void writePage(PrintWriter pw, String server, int port, int rpcs) {
+ pw.println("<html>");
+ pw.println("<head>");
+ pw.println("<title>CensusClientz</title>");
+ pw.println("<style>");
+ pw.println("label { display: inline-block; width: 5em; }");
+ pw.println("td { font-weight:normal; text-align:center; color:#38761d; }");
+ pw.println("th { font-weight:normal; text-align:center; color:#23238e; }");
+ pw.println("</style>");
+ pw.println("</head>");
+ pw.println("<body bgcolor=#ffffff>");
+ pw.println("<h1>Census Client</h1>");
+ pw.println("<form method=\"get\" action=\"censusclientz\">");
+
+ pw.println("<fieldset>");
+ pw.println("<legend>Census Client</legend>");
+
+ pw.println("<p>");
+ pw.println("<label for=\"server\">Server:</label>");
+ pw.println("<input type=\"text\" name=\"server\" id=\"server\"value=\"" + server + "\"/>");
+
+ pw.println("<p>");
+ pw.println("<label for=\"port\">Port:</label>");
+ pw.println("<input type=\"text\" name=\"port\" id=\"port\"value=\"" + port + "\"/>");
+
+ pw.println("<p>");
+ pw.println("<label for=\"rpcs\">RPCs:</label>");
+ pw.println("<select name=\"rpcs\">");
+ pw.println("<option " + (rpcs == 16 ? "selected " : "") + "value=16>16</option>");
+ pw.println("<option " + (rpcs == 128 ? "selected " : "") + "value=128>128</option>");
+ pw.println("<option " + (rpcs == 256 ? "selected " : "") + "value=256>256</option>");
+ pw.println("</select>");
+ pw.println("</fieldset>");
+
+ pw.println("<p><input type=\"submit\"/></p>");
+
+ for (CensusApplication.GolSpec gol : CensusApplication.gols) {
+ pw.println("<p>");
+ pw.println("<fieldset>");
+ pw.println("<legend>RPC " + getTitle(gol.dim, gensPerRpc) + "</legend>");
+ pw.println("<ul>");
+ pw.println("<li><strong>Tag:</strong> "
+ + CensusApplication.CLIENT_KEY + ":x"
+ + CensusApplication.getTagValue(gol.dim, gensPerRpc));
+ pw.println("<li><strong>Dimensions:</strong> " + gol.dim + "x" + gol.dim);
+ pw.println("<li><strong>Generations per RPC:</strong> " + gensPerRpc);
+ pw.println("<li><strong>Result:</strong> ");
+ pw.println(formatResultAsTable(gol.initGen, gol.currentGen, gol.dim, gol.gens));
+ pw.println("</ul>");
+ pw.println("</fieldset>");
+ }
+ pw.println("</form>");
+ pw.println("</body>");
+ pw.println("</html>");
+ }
+
+ private static String getTitle(int dim, int gensPerRpc) {
+ return dim + "x" + dim + " Board/" + gensPerRpc + " Generations Per RPC";
+ }
+
+ private static String formatResultAsTable(String initGen, String currGen, int dim, long gens) {
+ StringBuilder retval = new StringBuilder();
+ retval.append("<table>");
+ int initIndex = 0;
+ int currIndex = 0;
+ retval.append("<tr>");
+ retval.append("<td style=\"font-weight:bold;\" colspan=\"")
+ .append(dim + 1).append("\">InitGen</td>");
+ retval.append("<th style=\"font-weight:bold;\" colspan=\"")
+ .append(dim).append("\">Gen ").append(gens).append("</th>");
+ retval.append("</tr>");
+ for (int row = 0; row < dim; ++row) {
+ retval.append("<tr>");
+ for (int col = 0; col < dim; ++col) {
+ retval.append("<td>");
+ if (initGen.charAt(initIndex) == '1') {
+ retval.append("*");
+ } else {
+ retval.append("&nbsp");
+ }
+ retval.append("</td>");
+ ++initIndex;
+ }
+ retval.append("<td> &nbsp </td>");
+ for (int col = 0; col < dim; ++col) {
+ retval.append("<th>");
+ if (currGen.charAt(currIndex) == '1') {
+ retval.append("*");
+ } else {
+ retval.append("&nbsp");
+ }
+ retval.append("</th>");
+ ++currIndex;
+ }
+ retval.append("</tr>");
+ }
+ retval.append("</table>");
+ return retval.toString();
+ }
+
+ private static String getParamValue(Map<String, String[]> params, String name) {
+ String[] values = params.get(name);
+ if (values == null || values.length == 0) {
+ return null;
+ }
+ return values[values.length - 1];
+ }
+
+ private static String getParamValueOrDefault(
+ Map<String, String[]> params, String name, String defaultValue) {
+ String val = getParamValue(params, name);
+ return (val == null) ? defaultValue : val;
+ }
+
+ private static int getIntOrDefault(String s, int dflt) {
+ try {
+ return Integer.parseInt(s);
+ } catch (NumberFormatException exn) {
+ return dflt;
+ }
+ }
+}
diff --git a/examples/java/com/google/census/examples/gol/CensusServer.java b/examples/java/com/google/census/examples/gol/CensusServer.java
new file mode 100644
index 00000000..875562ef
--- /dev/null
+++ b/examples/java/com/google/census/examples/gol/CensusServer.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2016, 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 com.google.census.examples.gol;
+
+import com.google.census.CensusScope;
+import com.google.census.CensusServerInterceptor;
+import com.google.census.TagKey;
+import com.google.census.TagMap;
+import com.google.census.examples.CommandProcessorGrpc;
+import com.google.census.examples.CommandRequest;
+import com.google.census.examples.CommandResponse;
+import com.google.common.flags.Flag;
+import com.google.common.flags.FlagSpec;
+import com.google.common.flags.Flags;
+import com.google.net.rpc3.server.RpcServer;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.ServerInterceptors;
+import io.grpc.stub.StreamObserver;
+
+import java.util.logging.Logger;
+
+/**
+ * Census Game of Life Server.
+ * <p>
+ * Requests from the client are expected to be Strings of the form:
+ * <p>
+ * {@code gol <integer number of generations> <string encoding of initial generation>}
+ * <p>
+ * The server parses the input then uses the GameOfLife class to return
+ * a string encoding of the initial generation after the specified
+ * number of generations.
+ *
+ * @author dpo@google.com (Dino Oliva)
+ */
+public class CensusServer {
+ private static final Logger logger = Logger.getLogger(CensusServer.class.getName());
+ private static final TagKey SERVER_KEY = new TagKey("census_gol_server");
+
+ private final Server server;
+
+ @FlagSpec(
+ name = "port",
+ help = "The Game-of-Life server stubby port.")
+ private static final Flag<Integer> port = Flag.value(10000);
+
+ /* The port on which the server should run */
+ @FlagSpec(
+ name = "gol_server_grpc_port",
+ help = "The Game-of-Life server grpc port.")
+ private static final Flag<Integer> golServerGrpcPort = Flag.value(20000);
+
+ CensusServer() throws Exception {
+ server = ServerBuilder.forPort(golServerGrpcPort.get())
+ .addService(ServerInterceptors.intercept(CommandProcessorGrpc.bindService(
+ new CommandProcessorImpl()), new CensusServerInterceptor()))
+ .build()
+ .start();
+ logger.info("Server started, listening on " + golServerGrpcPort.get());
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ // Use stderr here since the logger may have been reset by its JVM shutdown hook.
+ System.err.println("*** shutting down gRPC server since JVM is shutting down");
+ CensusServer.this.stop();
+ System.err.println("*** server shut down");
+ }
+ });
+ }
+
+ private void stop() {
+ if (server != null) {
+ server.shutdown();
+ }
+ }
+
+ /**
+ * Await termination on the main thread since the grpc library uses daemon threads.
+ */
+ private void blockUntilShutdown() throws InterruptedException {
+ if (server != null) {
+ server.awaitTermination();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ // TODO(dpo): this call is necessary to run the completion hook to enable CensusNative. In
+ // general, G3 services built with gRPC will need to do this otherwise many libries will not
+ // work correctly. This issue needs to be resolved with the gRPC team.
+ Flags.parse(args);
+
+ // TODO(dpo): remove stubby-based server for monitoring hooks once gRPC team has implemented
+ // direct support.
+ RpcServer.newBuilder(port.get()).createAndStart();
+ logger.info("Server started, monitoring hooks on " + port.get());
+ CensusServer server = new CensusServer();
+ server.blockUntilShutdown();
+ }
+
+ private class CommandProcessorImpl implements CommandProcessorGrpc.CommandProcessor {
+ @Override
+ public void execute(CommandRequest request, StreamObserver<CommandResponse> responseObserver) {
+ String retval = executeCommand(request.getReq());
+ CommandResponse response = CommandResponse.newBuilder().setRetval(retval).build();
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+ }
+
+ private static String executeCommand(String command) {
+ String[] decode = command.split(" ");
+ if (decode.length == 3 && decode[0].equals("gol")) {
+ int gens = toIntWithDefault(decode[1], 0);
+ int dims = (int) Math.sqrt(decode[2].length());
+ if (dims * dims == decode[2].length()) {
+ // add server tag
+ TagMap tags = TagMap.of(SERVER_KEY, genTagValue(dims, gens));
+ try (CensusScope scope = new CensusScope(tags)) {
+ return new GameOfLife(dims, decode[2]).calcNextGenerations(gens).encode();
+ }
+ }
+ }
+ return "Error: bad request";
+ }
+
+ private static String genTagValue(int dim, int gens) {
+ return dim + "x" + dim + "-" + gens;
+ }
+
+ private static int toIntWithDefault(String s, int i) {
+ try {
+ return Integer.valueOf(s);
+ } catch (NumberFormatException exn) {
+ return i;
+ }
+ }
+
+}
diff --git a/examples/java/com/google/census/examples/gol/GameOfLife.java b/examples/java/com/google/census/examples/gol/GameOfLife.java
new file mode 100644
index 00000000..6d34ce92
--- /dev/null
+++ b/examples/java/com/google/census/examples/gol/GameOfLife.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2016, 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 com.google.census.examples.gol;
+
+/**
+ * This class implements Conway's Game of Life. The Game of Life consists of
+ * a matrix of cells that are either alive or dead. The next generation is
+ * calculated from the current generation based on the following rules:
+ * <ol>
+ * <li> If a cell is alive in the current generation, it will be alive in the
+ * next generation iff it has less than 2 live neighbors or more than 3
+ * live neighbors in the current generation.
+ * <li> If a cell is dead in the current generation, it will be alive in the
+ * next generation iff it has exactly 3 live neighbors in the current
+ * generation.
+ * </ol>
+ * This implementation is restricted to square matricies.
+ * <p>
+ * @author dpo@google.com (Dino Oliva)
+ */
+public class GameOfLife {
+ // Square matrix representation of the current generation - a live cell is
+ // represented by 'true' and a dead cell by 'false'.
+ private final boolean[][] generation;
+
+ // Dimension of the square matrix.
+ final int dimension;
+
+ /**
+ * Creates an instance of the GameOfLife with the current generation
+ * initalized to the input layout String. The layout is assumed to be in
+ * row-major order with character '0' representing false and any other
+ * character (typically '1') representing true.
+ * <p>
+ * Note that if the length of the input is not a square, the dimensions
+ * of the generation is set to 0.
+ *
+ * @param layout Specification of the initial generation.
+ * @return instance with the current generation initialized as specified.
+ */
+ public GameOfLife(int dimension, String layout) {
+ this.dimension = dimension;
+ generation = new boolean[dimension][dimension];
+ int index = 0;
+ for (int row = 0; row < dimension; ++row) {
+ for (int col = 0; col < dimension; ++col) {
+ generation[row][col] = toBool(layout.charAt(index));
+ ++index;
+ }
+ }
+ }
+
+ // Creates an instance of the GameOfLife with the specified layout and dimension.
+ // Assumes that 'generation' is a square matrix of dimension 'dimension'.
+ private GameOfLife(boolean[][] generation, int dimension) {
+ this.dimension = dimension;
+ this.generation = generation;
+ }
+
+ /**
+ * Encodes the current generation as a string in row-major order with
+ * character '1' to representing true and '0' to representing false.
+ *
+ * @return String encoding of the current generation.
+ */
+ public String encode() {
+ StringBuilder result = new StringBuilder(dimension * dimension);
+ for (int row = 0; row < dimension; ++row) {
+ for (int col = 0; col < dimension; ++col) {
+ result.append(generation[row][col] ? "1" : "0");
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Updates current generation the input number of times.
+ *
+ * @param gens the number of generations to calculate.
+ */
+ public GameOfLife calcNextGenerations(int gens) {
+ GameOfLife result = this;
+ for (int i = 0; i < gens; ++i) {
+ result = result.calcNextGeneration();
+ }
+ return result;
+ }
+
+ // Calculates the next generation based on the current generation then updates
+ // the current generation.
+ private GameOfLife calcNextGeneration() {
+ boolean[][] nextGeneration = new boolean[dimension][dimension];
+ // Calculates the value of each cell in the next generation based
+ // on it's direct neighbors in the current generation.
+ for (int row = 0; row < dimension; ++row) {
+ for (int col = 0; col < dimension; ++col) {
+ nextGeneration[row][col] = calcCell(row, col);
+ }
+ }
+ return new GameOfLife(nextGeneration, dimension);
+ }
+
+ // Calculates whether the cell specified by the input row and column is dead
+ // or alive in the next generation based on it's neighbors in the current
+ // generation. The rules are:
+ //
+ // 1. If the cell is alive in the current generation, it will be alive in the
+ // next generation iff it has less than 2 live neighbors or more than 3
+ // live neighbors in the current generation.
+ //
+ // 2. If the cell is dead in the current generation, it will be alive in the
+ // next generation iff it has exactly 3 live neighbors in the current
+ // generation.
+ //
+ // Note that this calculation uses wrap-around when counting neighbors such
+ // that the bottom row uses the top row (and vice-versa) and the left-most
+ // column use the right-most column (and vice-versa) as neighbors.
+ private boolean calcCell(int row, int col) {
+ int liveNeighbors = 0;
+ // This loop would range from -1 to 1 except that Java's modulo operator
+ // returns a value with the same sign as the numerator, so 'dimension' is
+ // added to ensure that the numerator is always positive.
+ for (int rowX = dimension - 1; rowX < dimension + 2; ++rowX) {
+ for (int colX = dimension - 1; colX < dimension + 2; ++colX) {
+ if (generation[(row + rowX) % dimension][(col + colX) % dimension]) {
+ liveNeighbors++;
+ }
+ }
+ }
+ if (generation[row][col]) {
+ liveNeighbors--;
+ if (liveNeighbors < 2 || liveNeighbors > 3) {
+ return false;
+ }
+ return true;
+ } else {
+ if (liveNeighbors == 3) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private static boolean toBool(char c) {
+ return c != '0';
+ }
+}
diff --git a/examples/java/com/google/census/examples/gol/census_gol.borg b/examples/java/com/google/census/examples/gol/census_gol.borg
new file mode 100644
index 00000000..86e6c762
--- /dev/null
+++ b/examples/java/com/google/census/examples/gol/census_gol.borg
@@ -0,0 +1,51 @@
+import "//production/borg/templates/java/java.borg" as java
+import "//production/borg/templates/common.borg" as common
+
+vars {
+ cell = 'wk' // Borg cell to run client and server jobs in.
+}
+
+service census-gol = {
+ params {
+ cell = vars.cell
+ google3 = replace(getcwd(), 'google3.*', 'google3')
+ binary_base_dir = "%google3%/blaze-bin/third_party/java_src/census/examples/java/com/google/census/examples/gol" // NOLINT
+ }
+
+ template job census_gol_job = java.java_job {
+ args {
+ port = '%port%'
+ census_cpu_accounting_enabled = true
+ census_enabled = true
+ census_whitelist_regexp_for_resourcez_services = '.*'
+ rpc_census_record_stats = true
+ }
+
+ appclass {
+ type = 'BEST_EFFORT'
+ }
+
+ binary = "%params.binary_base_dir%/%gol_binary_name%"
+
+ requirements {
+ ram = 2G
+ disk = 500M
+ cpu = 1
+ }
+
+ runtime {
+ cell = params.cell
+ }
+ }
+
+ job client = census_gol_job {
+ gol_binary_name = 'CensusClient_deploy.jar'
+ }
+
+ job server = census_gol_job {
+ gol_binary_name = 'CensusServer_deploy.jar'
+ args {
+ gol_server_grpc_port = '%port_grpc%'
+ }
+ }
+}