diff --git a/dev/com.ibm.websphere.appserver.features/visibility/auto/io.openliberty.grpcMonitor-1.0.feature b/dev/com.ibm.websphere.appserver.features/visibility/auto/io.openliberty.grpcMonitor-1.0.feature
new file mode 100644
index 00000000000..5301c4ea760
--- /dev/null
+++ b/dev/com.ibm.websphere.appserver.features/visibility/auto/io.openliberty.grpcMonitor-1.0.feature
@@ -0,0 +1,12 @@
+-include= ~${workspace}/cnf/resources/bnd/feature.props
+symbolicName=io.openliberty.grpcMonitor-1.0
+Manifest-Version: 1.0
+IBM-API-Package: io.openliberty.grpc.monitor; type="ibm-api"
+IBM-Provision-Capability: \
+ osgi.identity; filter:="(&(type=osgi.subsystem.feature)(osgi.identity=com.ibm.websphere.appserver.monitor-1.0))", \
+ osgi.identity; filter:="(&(type=osgi.subsystem.feature)(|(osgi.identity=io.openliberty.grpc-1.0)(osgi.identity=io.openliberty.grpcClient-1.0)))"
+IBM-Install-Policy: when-satisfied
+-bundles=io.openliberty.grpc.1.0.internal.monitor
+kind=beta
+edition=full
+
diff --git a/dev/com.ibm.ws.grpc_fat/fat/src/com/ibm/ws/fat/grpc/FATSuite.java b/dev/com.ibm.ws.grpc_fat/fat/src/com/ibm/ws/fat/grpc/FATSuite.java
index 54a0116e084..7e0916f5b22 100644
--- a/dev/com.ibm.ws.grpc_fat/fat/src/com/ibm/ws/fat/grpc/FATSuite.java
+++ b/dev/com.ibm.ws.grpc_fat/fat/src/com/ibm/ws/fat/grpc/FATSuite.java
@@ -18,6 +18,7 @@
@SuiteClasses({
HelloWorldCDITests.class,
HelloWorldTest.class,
+ GrpcMetricsTest.class,
ServiceSupportTests.class,
ServiceConfigTests.class,
ServiceInterceptorTests.class,
diff --git a/dev/com.ibm.ws.grpc_fat/fat/src/com/ibm/ws/fat/grpc/GrpcMetricsTest.java b/dev/com.ibm.ws.grpc_fat/fat/src/com/ibm/ws/fat/grpc/GrpcMetricsTest.java
new file mode 100644
index 00000000000..b2868718fca
--- /dev/null
+++ b/dev/com.ibm.ws.grpc_fat/fat/src/com/ibm/ws/fat/grpc/GrpcMetricsTest.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package com.ibm.ws.fat.grpc;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
+import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
+import com.ibm.websphere.simplicity.ShrinkHelper;
+import com.ibm.websphere.simplicity.log.Log;
+
+import componenttest.annotation.Server;
+import componenttest.custom.junit.runner.FATRunner;
+import componenttest.topology.impl.LibertyServer;
+import componenttest.topology.utils.FATServletClient;
+
+@RunWith(FATRunner.class)
+public class GrpcMetricsTest extends FATServletClient {
+
+ protected static final Class> c = GrpcMetricsTest.class;
+
+ @Rule
+ public TestName name = new TestName();
+
+ @Server("HelloWorldServer")
+ public static LibertyServer grpcMetricsServer; //GrpcMetricsServer
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // add all classes from com.ibm.ws.grpc.fat.helloworld.service and io.grpc.examples.helloworld
+ // to a new app HelloWorldService.war
+ ShrinkHelper.defaultDropinApp(grpcMetricsServer, "HelloWorldService.war",
+ "com.ibm.ws.grpc.fat.helloworld.service",
+ "io.grpc.examples.helloworld");
+
+ // add all classes from com.ibm.ws.grpc.fat.helloworld.client, io.grpc.examples.helloworld,
+ // and com.ibm.ws.fat.grpc.tls to a new app HelloWorldClient.war.
+ ShrinkHelper.defaultDropinApp(grpcMetricsServer, "HelloWorldClient.war",
+ "com.ibm.ws.grpc.fat.helloworld.client",
+ "io.grpc.examples.helloworld",
+ "com.ibm.ws.fat.grpc.tls");
+
+ grpcMetricsServer.startServer(GrpcMetricsTest.class.getSimpleName() + ".log");
+ assertNotNull("CWWKO0219I.*ssl not recieved", grpcMetricsServer.waitForStringInLog("CWWKO0219I.*ssl"));
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ grpcMetricsServer.stopServer();
+ }
+
+ /**
+ * Tests gRPC server-side and client-side metrics.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testGrpcMetrics() throws Exception {
+ String contextRoot = "HelloWorldClient";
+ try (WebClient webClient = new WebClient()) {
+
+ // Construct the URL for the test
+ URL url = GrpcTestUtils.createHttpUrl(grpcMetricsServer, contextRoot, "grpcClient");
+
+ HtmlPage page = (HtmlPage) webClient.getPage(url);
+
+ // Log the page for debugging if necessary in the future.
+ Log.info(c, name.getMethodName(), page.asText());
+ Log.info(c, name.getMethodName(), page.asXml());
+
+ assertTrue("the servlet was not loaded correctly",
+ page.asText().contains("gRPC helloworld client example"));
+
+ HtmlForm form = page.getFormByName("form1");
+
+ // set a name, which we'll expect the RPC to return
+ HtmlTextInput inputText = (HtmlTextInput) form.getInputByName("user");
+ inputText.setValueAttribute("us3r1");
+
+ // set the port
+ HtmlTextInput inputPort = (HtmlTextInput) form.getInputByName("port");
+ inputPort.setValueAttribute(String.valueOf(grpcMetricsServer.getHttpDefaultPort()));
+
+ // set the hostname
+ HtmlTextInput inputHost = (HtmlTextInput) form.getInputByName("address");
+ inputHost.setValueAttribute(grpcMetricsServer.getHostname());
+
+ // submit, and execute the RPC
+ HtmlSubmitInput submitButton = form.getInputByName("submit");
+ page = submitButton.click();
+
+ // check the gRPC client-side metrics
+ checkMetric("/metrics/vendor/grpc.client.rpcStarted.total", "1");
+ checkMetric("/metrics/vendor/grpc.client.rpcCompleted.total", "1");
+ checkMetric("/metrics/vendor/grpc.client.sentMessages.total", "1");
+ checkMetric("/metrics/vendor/grpc.client.receivedMessages.total", "1");
+
+ // check the gRPC server-side metrics
+ checkMetric("/metrics/vendor/grpc.server.rpcStarted.total", "1");
+ checkMetric("/metrics/vendor/grpc.server.rpcCompleted.total", "1");
+ checkMetric("/metrics/vendor/grpc.server.sentMessages.total", "1");
+ checkMetric("/metrics/vendor/grpc.server.receivedMessages.total", "1");
+
+ // Log the page for debugging if necessary in the future.
+ Log.info(c, name.getMethodName(), page.asText());
+ assertTrue("the gRPC request did not complete correctly", page.asText().contains("us3r1"));
+ }
+ }
+
+ /**
+ * Verifies the given metric by comparing the actual value with the given value
+ *
+ * @param metricName - the metric to verify
+ * @param expectedValue - the expected value
+ * @return the actual value received from the Metrics endpoint
+ */
+ private String checkMetric(String metricName, String expectedValue) {
+ HttpURLConnection con = null;
+ try {
+ URL url = new URL("http://" + grpcMetricsServer.getHostname() + ":" + grpcMetricsServer.getHttpDefaultPort() + metricName);
+ int retcode;
+ con = (HttpURLConnection) url.openConnection();
+ con.setDoInput(true);
+ con.setDoOutput(true);
+ con.setUseCaches(false);
+ con.setRequestMethod("GET");
+
+ retcode = con.getResponseCode();
+ if (retcode != 200) {
+ fail("Bad return code from Metrics method call. Expected 200, got " + retcode);
+
+ return null;
+ }
+
+ InputStream is = con.getInputStream();
+ InputStreamReader isr = new InputStreamReader(is);
+
+ BufferedReader br = new BufferedReader(isr);
+
+ String metricValue = null;
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ if (!line.startsWith("#")) {
+ String[] mertricAttr = line.split(" ");
+ if (mertricAttr.length > 0) {
+ metricValue = mertricAttr[mertricAttr.length - 1];
+ break;
+ }
+ }
+ }
+
+ if (metricValue == null || !metricValue.equals(expectedValue)) {
+ fail(String.format("Incorrect metric value [%s]. Expected [%s], got [%s]", metricName, expectedValue, metricValue));
+ }
+ return metricValue;
+
+ } catch (Exception e) {
+ fail("Caught unexpected exception: " + e);
+ return null;
+ } finally {
+ if (con != null) {
+ con.disconnect();
+ }
+ }
+
+ }
+}
diff --git a/dev/com.ibm.ws.grpc_fat/publish/servers/ConsumerServer/server.xml b/dev/com.ibm.ws.grpc_fat/publish/servers/ConsumerServer/server.xml
index b3c63f93ce3..814fdc44287 100644
--- a/dev/com.ibm.ws.grpc_fat/publish/servers/ConsumerServer/server.xml
+++ b/dev/com.ibm.ws.grpc_fat/publish/servers/ConsumerServer/server.xml
@@ -70,6 +70,7 @@
+
-
\ No newline at end of file
+
diff --git a/dev/com.ibm.ws.grpc_fat/publish/servers/GrpcServer/server.xml b/dev/com.ibm.ws.grpc_fat/publish/servers/GrpcServer/server.xml
index 317ca3ca084..27f85a4e36f 100644
--- a/dev/com.ibm.ws.grpc_fat/publish/servers/GrpcServer/server.xml
+++ b/dev/com.ibm.ws.grpc_fat/publish/servers/GrpcServer/server.xml
@@ -17,4 +17,4 @@
-
\ No newline at end of file
+
diff --git a/dev/com.ibm.ws.grpc_fat/publish/servers/GrpcServerOnly/server.xml b/dev/com.ibm.ws.grpc_fat/publish/servers/GrpcServerOnly/server.xml
index 6c45d0ed1dc..fef4093c76c 100644
--- a/dev/com.ibm.ws.grpc_fat/publish/servers/GrpcServerOnly/server.xml
+++ b/dev/com.ibm.ws.grpc_fat/publish/servers/GrpcServerOnly/server.xml
@@ -16,4 +16,4 @@
-
\ No newline at end of file
+
diff --git a/dev/com.ibm.ws.grpc_fat/publish/servers/HelloWorldServer/server.xml b/dev/com.ibm.ws.grpc_fat/publish/servers/HelloWorldServer/server.xml
index 598ada483b9..8edaac23a25 100644
--- a/dev/com.ibm.ws.grpc_fat/publish/servers/HelloWorldServer/server.xml
+++ b/dev/com.ibm.ws.grpc_fat/publish/servers/HelloWorldServer/server.xml
@@ -7,17 +7,23 @@
Contributors:
IBM Corporation - initial API and implementation
- -->
+-->
grpc-1.0
grpcClient-1.0
ssl-1.0
+ monitor-1.0
+ mpMetrics-2.3
+
-
\ No newline at end of file
+
+
diff --git a/dev/com.ibm.ws.grpc_fat/publish/servers/ProducerServer/server.xml b/dev/com.ibm.ws.grpc_fat/publish/servers/ProducerServer/server.xml
index 1b90a812f15..dce7426270d 100644
--- a/dev/com.ibm.ws.grpc_fat/publish/servers/ProducerServer/server.xml
+++ b/dev/com.ibm.ws.grpc_fat/publish/servers/ProducerServer/server.xml
@@ -36,4 +36,4 @@
traceSpecification="*=info=enabled:io.openliberty.grpc*=all:com.ibm.testapp.g3store*=all"
maxFileSize="40" maxFiles="20" traceFormat="BASIC" />
-
\ No newline at end of file
+
diff --git a/dev/com.ibm.ws.grpc_fat/publish/servers/StoreServer/server.xml b/dev/com.ibm.ws.grpc_fat/publish/servers/StoreServer/server.xml
index 3e19a7fd2c0..9006ff8a521 100644
--- a/dev/com.ibm.ws.grpc_fat/publish/servers/StoreServer/server.xml
+++ b/dev/com.ibm.ws.grpc_fat/publish/servers/StoreServer/server.xml
@@ -64,4 +64,4 @@
-
\ No newline at end of file
+
diff --git a/dev/com.ibm.ws.grpc_fat/publish/servers/StreamingServer/server.xml b/dev/com.ibm.ws.grpc_fat/publish/servers/StreamingServer/server.xml
index 2ae6a060051..a1f1e9a1c46 100644
--- a/dev/com.ibm.ws.grpc_fat/publish/servers/StreamingServer/server.xml
+++ b/dev/com.ibm.ws.grpc_fat/publish/servers/StreamingServer/server.xml
@@ -19,4 +19,4 @@
-
\ No newline at end of file
+
diff --git a/dev/com.ibm.ws.grpc_fat/test-applications/HelloWorldClient.war/src/com/ibm/ws/grpc/fat/helloworld/client/HelloWorldClientServlet.java b/dev/com.ibm.ws.grpc_fat/test-applications/HelloWorldClient.war/src/com/ibm/ws/grpc/fat/helloworld/client/HelloWorldClientServlet.java
index 1fc3b9fe1f4..e338de03977 100644
--- a/dev/com.ibm.ws.grpc_fat/test-applications/HelloWorldClient.war/src/com/ibm/ws/grpc/fat/helloworld/client/HelloWorldClientServlet.java
+++ b/dev/com.ibm.ws.grpc_fat/test-applications/HelloWorldClient.war/src/com/ibm/ws/grpc/fat/helloworld/client/HelloWorldClientServlet.java
@@ -146,4 +146,4 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
stopService();
}
-}
\ No newline at end of file
+}
diff --git a/dev/com.ibm.ws.microprofile.metrics.2.3.monitor/src/com/ibm/ws/microprofile/metrics/monitor/MappingTable.java b/dev/com.ibm.ws.microprofile.metrics.2.3.monitor/src/com/ibm/ws/microprofile/metrics/monitor/MappingTable.java
index 101b10ae728..0d26fb64546 100644
--- a/dev/com.ibm.ws.microprofile.metrics.2.3.monitor/src/com/ibm/ws/microprofile/metrics/monitor/MappingTable.java
+++ b/dev/com.ibm.ws.microprofile.metrics.2.3.monitor/src/com/ibm/ws/microprofile/metrics/monitor/MappingTable.java
@@ -40,6 +40,8 @@ public class MappingTable {
public static final String SESSION_TAG_NAME = "appname";
public static final String JAXWS_SERVER_TAG_NAME = "endpoint";
public static final String JAXWS_CLIENT_TAG_NAME = "endpoint";
+ public static final String GRPC_SERVER_TAG_NAME = "grpc";
+ public static final String GRPC_CLIENT_TAG_NAME = "grpc";
public static final String COUNTER = MetricType.COUNTER.toString().toUpperCase();
public static final String GAUGE = MetricType.GAUGE.toString().toUpperCase();
@@ -117,6 +119,22 @@ private MappingTable() {
{ "vendor", "jaxws.client.responseTime.total", "Total Response Time", "jaxws.responseTime.total.description", GAUGE, MetricUnits.MILLISECONDS, "TotalHandlingTime", null, JAXWS_CLIENT_TAG_NAME }
};
mappingTable.put("WebSphere:feature=jaxws,*,type=Performance.Counter.Client", jaxwsClientTable);
+
+ String[][] grpcServerTable = new String[][]{
+ { "vendor", "grpc.server.rpcStarted.total", "Total Server RPCs Started Count", "grpc.server.rpcStarted.total.description", COUNTER, MetricUnits.NONE, "RpcStartedCount", null, GRPC_SERVER_TAG_NAME },
+ { "vendor", "grpc.server.rpcCompleted.total", "Total Server RPCs Completed Count", "grpc.server.rpcCompleted .total.description", COUNTER, MetricUnits.NONE, "RpcCompletedCount", null, GRPC_SERVER_TAG_NAME },
+ { "vendor", "grpc.server.sentMessages.total", "Total Sent Stream Messages", "grpc.server.sentMessages.total.description", COUNTER, MetricUnits.NONE, "SentMessagesCount", null, GRPC_SERVER_TAG_NAME },
+ { "vendor", "grpc.server.receivedMessages.total", "Total Received Stream Messages", "grpc.server.receivedMessages.total.description", COUNTER, MetricUnits.NONE, "ReceivedMessagesCount", null, GRPC_SERVER_TAG_NAME }
+ };
+ mappingTable.put("WebSphere:type=GrpcServerStats,name=*", grpcServerTable);
+
+ String[][] grpcClientTable = new String[][]{
+ { "vendor", "grpc.client.rpcStarted.total", "Total Client RPCs Started Count", "grpc.client.rpcStarted.total.description", COUNTER, MetricUnits.NONE, "RpcStartedCount", null, GRPC_SERVER_TAG_NAME },
+ { "vendor", "grpc.client.rpcCompleted.total", "Total Client RPCs Completed Count", "grpc.client.rpcCompleted.total.description", COUNTER, MetricUnits.NONE, "RpcCompletedCount", null, GRPC_SERVER_TAG_NAME },
+ { "vendor", "grpc.client.sentMessages.total", "Total Sent Stream Messages", "grpc.client.sentMessages.total.description", COUNTER, MetricUnits.NONE, "SentMessagesCount", null, GRPC_SERVER_TAG_NAME },
+ { "vendor", "grpc.client.receivedMessages.total", "Total Received Stream Messages", "grpc.client.receivedMessages.total.description", COUNTER, MetricUnits.NONE, "ReceivedMessagesCount", null, GRPC_SERVER_TAG_NAME }
+ };
+ mappingTable.put("WebSphere:type=GrpcClientStats,name=*", grpcClientTable);
}
diff --git a/dev/com.ibm.ws.microprofile.metrics.2.3/resources/com/ibm/ws/microprofile/metrics/resources/Metrics.nlsprops b/dev/com.ibm.ws.microprofile.metrics.2.3/resources/com/ibm/ws/microprofile/metrics/resources/Metrics.nlsprops
index 8f21ad11401..cafa581a2ab 100644
--- a/dev/com.ibm.ws.microprofile.metrics.2.3/resources/com/ibm/ws/microprofile/metrics/resources/Metrics.nlsprops
+++ b/dev/com.ibm.ws.microprofile.metrics.2.3/resources/com/ibm/ws/microprofile/metrics/resources/Metrics.nlsprops
@@ -128,6 +128,15 @@ session.activeSessions.description=The number of concurrently active sessions. (
session.invalidated.total.description=The number of sessions that have logged out since this metric was enabled.
session.invalidatedbyTimeout.total.description=The number of sessions that have logged out by timeout since this metric was enabled.
+grpc.server.rpcStarted.total.description=The total number of RPCs started on the server.
+grpc.server.rpcCompleted.total.description=The total number of RPCs completed on the server, regardless of success or failure.
+grpc.server.sentMessages.total.description=The total number of stream messages sent by the server.
+grpc.server.receivedMessages.total.description=The total number of stream messages received from the client.
+
+grpc.client.rpcStarted.total.description=The total number of RPCs started on the client.
+grpc.client.rpcCompleted.total.description=The total number of RPCs completed on the client, regardless of success or failure.
+grpc.client.sentMessages.total.description=The total number of stream messages sent by the client.
+grpc.client.receivedMessages.total.description=The total number of stream messages received from the server.
REST.request.description=The number of invocations and total response time of this RESTful resource method since the start of the server.
#-----------------------------------------------------------------------------------------------------------------------------
diff --git a/dev/io.openliberty.grpc.1.0.internal.client/bnd.bnd b/dev/io.openliberty.grpc.1.0.internal.client/bnd.bnd
index 18471329ff4..38bad331de2 100644
--- a/dev/io.openliberty.grpc.1.0.internal.client/bnd.bnd
+++ b/dev/io.openliberty.grpc.1.0.internal.client/bnd.bnd
@@ -34,13 +34,23 @@ Import-Package: !sun.*,\
!org.eclipse.jetty.*,\
!org.jboss.*,\
javax.annotation;version=!,\
+ !com.ibm.websphere.monitor.jmx,\
*
+# Use dynamic import of monitor packages to support indepndent/dynamic enablement of monitor feature
+DynamicImport-Package: com.ibm.websphere.monitor.meters;version="1.0.0", \
+ com.ibm.websphere.monitor.jmx;version="1.0.0", \
+ com.ibm.wsspi.request.probe.bci, \
+ com.ibm.wsspi.probeExtension, \
+ io.openliberty.grpc.internal.monitor
+
Export-Package: \
io.openliberty.grpc.internal.client.* ,\
io.grpc.netty.shaded.io.*
--dsannotations: io.openliberty.grpc.internal.client.config.GrpcClientConfigImpl
+-dsannotations: \
+ io.openliberty.grpc.internal.client.config.GrpcClientConfigImpl,\
+ io.openliberty.grpc.internal.client.GrpcClientComponent
# include the service providers and metatype
Include-Resource: \
@@ -65,16 +75,21 @@ Provide-Capability: osgi.serviceloader;osgi.serviceloader=io.grpc.ManagedChannel
instrument.disabled: true
-buildpath: \
+ com.ibm.ws.container.service;version=latest,\
com.ibm.websphere.javaee.servlet.4.0;version=latest,\
com.ibm.websphere.security;version=latest,\
com.ibm.ws.container.service;version=latest,\
com.ibm.ws.managedobject;version=latest,\
com.ibm.ws.webcontainer;version=latest,\
com.ibm.ws.logging.core;version=latest,\
+ com.ibm.ws.monitor;version=latest,\
com.ibm.websphere.org.osgi.core;version=latest,\
com.ibm.websphere.org.osgi.service.component;version=latest,\
com.ibm.ws.org.osgi.annotation.versioning;version=latest,\
com.ibm.wsspi.org.osgi.service.component.annotations;version=latest,\
+ com.ibm.ws.kernel.boot;version=latest,\
+ com.ibm.ws.kernel.service;version=latest,\
+ com.ibm.ws.kernel.feature;version=latest,\
org.osgi.service.component.annotations;version=latest,\
io.grpc:grpc-netty-shaded;version=1.27.0,\
io.openliberty.grpc.1.0.internal.common;version=latest,\
diff --git a/dev/io.openliberty.grpc.1.0.internal.client/src/io/openliberty/grpc/internal/client/GrpcClientComponent.java b/dev/io.openliberty.grpc.1.0.internal.client/src/io/openliberty/grpc/internal/client/GrpcClientComponent.java
new file mode 100644
index 00000000000..499c7cf97c0
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.client/src/io/openliberty/grpc/internal/client/GrpcClientComponent.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.client;
+
+import java.util.Set;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+
+import com.ibm.websphere.ras.Tr;
+import com.ibm.websphere.ras.TraceComponent;
+import com.ibm.ws.container.service.app.deploy.ApplicationInfo;
+import com.ibm.ws.container.service.state.ApplicationStateListener;
+import com.ibm.ws.container.service.state.StateChangeException;
+import com.ibm.ws.kernel.feature.FeatureProvisioner;
+import com.ibm.wsspi.kernel.service.utils.AtomicServiceReference;
+
+@Component(service = {
+ ApplicationStateListener.class }, configurationPolicy = ConfigurationPolicy.IGNORE, immediate = true)
+public class GrpcClientComponent implements ApplicationStateListener {
+
+ private static final TraceComponent tc = Tr.register(GrpcClientComponent.class, GrpcClientMessages.GRPC_TRACE_NAME,
+ GrpcClientMessages.GRPC_BUNDLE);
+
+ /** Indicates whether the monitor feature is enabled */
+ private static boolean monitoringEnabled = false;
+
+ private final String FEATUREPROVISIONER_REFERENCE_NAME = "featureProvisioner";
+
+ private final AtomicServiceReference featureProvisioner = new AtomicServiceReference(
+ FEATUREPROVISIONER_REFERENCE_NAME);
+
+ @Activate
+ protected void activate(ComponentContext cc) {
+ featureProvisioner.activate(cc);
+ }
+
+ @Deactivate
+ protected void deactivate(ComponentContext cc) {
+ featureProvisioner.deactivate(cc);
+ }
+
+ @Reference(name = FEATUREPROVISIONER_REFERENCE_NAME, service = FeatureProvisioner.class, cardinality = ReferenceCardinality.MANDATORY)
+ protected void setFeatureProvisioner(ServiceReference ref) {
+ featureProvisioner.setReference(ref);
+ }
+
+ protected void unsetFeatureProvisioner(ServiceReference ref) {
+ featureProvisioner.unsetReference(ref);
+ }
+
+ /**
+ * Set the indication whether the monitor feature is enabled
+ */
+ private void setMonitoringEnabled() {
+ Set currentFeatureSet = featureProvisioner.getService().getInstalledFeatures();
+ monitoringEnabled = currentFeatureSet.contains("monitor-1.0");
+ }
+
+ /**
+ * @return true
if the monitor feature is enabled,
+ * false
otherwise
+ */
+ public static boolean isMonitoringEnabled() {
+ return monitoringEnabled;
+ }
+
+ @Override
+ public void applicationStarting(ApplicationInfo appInfo) throws StateChangeException {
+ setMonitoringEnabled();
+ }
+
+ @Override
+ public void applicationStarted(ApplicationInfo appInfo) throws StateChangeException {
+ }
+
+ @Override
+ public void applicationStopping(ApplicationInfo appInfo) {
+ }
+
+ @Override
+ public void applicationStopped(ApplicationInfo appInfo) {
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.client/src/io/openliberty/grpc/internal/client/LibertyManagedChannelProvider.java b/dev/io.openliberty.grpc.1.0.internal.client/src/io/openliberty/grpc/internal/client/LibertyManagedChannelProvider.java
index fddcf7f3ba8..ee85acc4b07 100644
--- a/dev/io.openliberty.grpc.1.0.internal.client/src/io/openliberty/grpc/internal/client/LibertyManagedChannelProvider.java
+++ b/dev/io.openliberty.grpc.1.0.internal.client/src/io/openliberty/grpc/internal/client/LibertyManagedChannelProvider.java
@@ -71,6 +71,10 @@ private void configureLibertyBuilder(NettyChannelBuilder builder, String target)
private void addLibertyInterceptors(NettyChannelBuilder builder) {
builder.intercept(new LibertyClientInterceptor());
+ ClientInterceptor monitoringInterceptor = createMonitoringClientInterceptor();
+ if (monitoringInterceptor != null) {
+ builder.intercept(monitoringInterceptor);
+ }
}
private void addLibertySSLConfig(NettyChannelBuilder builder, String target) {
@@ -134,4 +138,24 @@ private void addUserInterceptors(NettyChannelBuilder builder, String target) {
}
}
}
+
+ private ClientInterceptor createMonitoringClientInterceptor() {
+ // create the interceptor only if the monitor feature is enabled
+ if (!GrpcClientComponent.isMonitoringEnabled()) {
+ return null;
+ }
+ ClientInterceptor interceptor = null;
+ // monitoring interceptor
+ final String className = "io.openliberty.grpc.internal.monitor.GrpcMonitoringClientInterceptor";
+ try {
+ Class> clazz = Class.forName(className);
+ interceptor = (ClientInterceptor) clazz.getDeclaredConstructor()
+ .newInstance();
+ } catch (Exception e) {
+ // an exception can happen if the monitoring package is not loaded
+ }
+
+ return interceptor;
+ }
+
}
diff --git a/dev/io.openliberty.grpc.1.0.internal.common/src/io/openliberty/grpc/internal/Utils.java b/dev/io.openliberty.grpc.1.0.internal.common/src/io/openliberty/grpc/internal/Utils.java
index 8c2e2bbbadb..aa0cb78fa8c 100644
--- a/dev/io.openliberty.grpc.1.0.internal.common/src/io/openliberty/grpc/internal/Utils.java
+++ b/dev/io.openliberty.grpc.1.0.internal.common/src/io/openliberty/grpc/internal/Utils.java
@@ -13,12 +13,6 @@
import static io.grpc.internal.GrpcUtil.CONTENT_TYPE_KEY;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.ibm.websphere.ras.TraceComponent;
-import com.ibm.ws.ffdc.FFDCFilter;
-
import io.grpc.internal.GrpcUtil;
public class Utils {
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/.classpath b/dev/io.openliberty.grpc.1.0.internal.monitor/.classpath
new file mode 100644
index 00000000000..173791f0fa3
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/.project b/dev/io.openliberty.grpc.1.0.internal.monitor/.project
new file mode 100644
index 00000000000..b5df983fde8
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/.project
@@ -0,0 +1,23 @@
+
+
+ io.openliberty.grpc.1.0.internal.monitor
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ bndtools.core.bndbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ bndtools.core.bndnature
+
+
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/bnd.bnd b/dev/io.openliberty.grpc.1.0.internal.monitor/bnd.bnd
new file mode 100644
index 00000000000..99cbef22f58
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/bnd.bnd
@@ -0,0 +1,58 @@
+#*******************************************************************************
+# Copyright (c) 2020 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+#*******************************************************************************
+-include= ~../cnf/resources/bnd/bundle.props
+bVersion=1.0
+
+Bundle-Name: gRPC Monitor
+Bundle-SymbolicName: io.openliberty.grpc.1.0.internal.monitor
+Bundle-Description: gRPC Monitor, version ${bVersion}
+
+# Using version=! in order to not have a version attached to the import for packages that were removed
+# from Java after Java 8. Doing this keeps the import like before Java 11 support. It will get the
+# packages from Java when using Java 8 or earlier and from the new shipped bundles for Java 9 and later.
+Import-Package: \
+ !*.internal.*,*
+
+Export-Package: \
+ io.openliberty.grpc.monitor,\
+ io.openliberty.grpc.internal.monitor
+
+
+DynamicImport-Package: com.ibm.websphere.monitor.meters;version="1.0.0", \
+ com.ibm.websphere.monitor.annotation;version="1.0.0", \
+ com.ibm.websphere.monitor.jmx;version="1.0.0", \
+ com.ibm.ws.pmi.server;version="[1.1,2)", \
+ com.ibm.ws.pmi.stat;version="[1.1,2)", \
+ com.ibm.websphere.pmi;version="[1.1,2)", \
+ com.ibm.wsspi.pmi.factory;version="[1.1,2)", \
+ com.ibm.wsspi.pmi.stat;version="[1.1,2)"
+
+Liberty-Monitoring-Components: io.openliberty.grpc.internal.monitor.GrpcServerMonitor, \
+ io.openliberty.grpc.internal.monitor.GrpcClientMonitor
+
+instrument.disabled: true
+
+-buildpath: \
+ com.ibm.ws.logging.core,\
+ com.ibm.websphere.org.osgi.core;version=latest,\
+ com.ibm.websphere.org.osgi.service.component;version=latest,\
+ com.ibm.ws.monitor;version=latest,\
+ com.ibm.ws.org.osgi.annotation.versioning;version=latest,\
+ com.ibm.wsspi.org.osgi.service.component.annotations;version=latest,\
+ com.google.api.grpc:proto-google-common-protos;version=1.17.0, \
+ com.google.code.findbugs:jsr305;version=3.0.2,\
+ com.google.guava:failureaccess;version=1.0.1,\
+ com.google.guava:guava;version=27.0.1, \
+ com.google.j2objc:j2objc-annotations;version=1.1, \
+ com.google.protobuf:protobuf-java;version=3.11.1, \
+ org.checkerframework:checker-compat-qual;version=2.5.2,\
+ io.openliberty.grpc.1.0.internal.common;version=latest,\
+ org.osgi.service.component.annotations;version=latest
\ No newline at end of file
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientMonitor.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientMonitor.java
new file mode 100644
index 00000000000..f7af803dcbf
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientMonitor.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import com.ibm.websphere.monitor.annotation.Monitor;
+import com.ibm.websphere.monitor.annotation.ProbeAtEntry;
+import com.ibm.websphere.monitor.annotation.ProbeAtReturn;
+import com.ibm.websphere.monitor.annotation.ProbeSite;
+import com.ibm.websphere.monitor.annotation.PublishedMetric;
+import com.ibm.websphere.monitor.annotation.This;
+import com.ibm.websphere.monitor.meters.MeterCollection;
+
+/**
+ * Monitor class for gRPC Client.
+ * This class is responsible for managing the gRPC client MXBean object, as well
+ * as the actual updating of the values of the counters defined in the MXBean
+ * object.
+ */
+@Monitor(group = "GrpcClient")
+public class GrpcClientMonitor {
+ private final static String METRIC_KEY_DELIMETER = "/";
+
+ @PublishedMetric
+ public MeterCollection grpcClientCountByName = new MeterCollection(
+ "GrpcClient", this);
+
+ @ProbeAtEntry
+ @ProbeSite(clazz = "io.openliberty.grpc.internal.monitor.GrpcClientStatsMonitor", method = "recordCallStarted")
+ public void atRpcCallStart(@This Object clientStats) {
+ GrpcClientStatsMonitor stats = (GrpcClientStatsMonitor) clientStats;
+ getGrpcClientStats(stats.getMethod()).recordCallStarted();
+ }
+
+ @ProbeAtEntry
+ @ProbeSite(clazz = "io.openliberty.grpc.internal.monitor.GrpcClientStatsMonitor", method = "recordClientHandled")
+ public void atGrpcClientHandled(@This Object clientStats) {
+ GrpcClientStatsMonitor stats = (GrpcClientStatsMonitor) clientStats;
+ getGrpcClientStats(stats.getMethod()).recordClientHandled();
+ }
+
+ @ProbeAtReturn
+ @ProbeSite(clazz = "io.openliberty.grpc.internal.monitor.GrpcClientStatsMonitor", method = "recordMsgReceived")
+ public void atClientMsgReceived(@This Object clientStats) {
+ GrpcClientStatsMonitor stats = (GrpcClientStatsMonitor) clientStats;
+ getGrpcClientStats(stats.getMethod()).incrementReceivedMsgCountBy(1);
+ }
+
+ @ProbeAtReturn
+ @ProbeSite(clazz = "io.openliberty.grpc.internal.monitor.GrpcClientStatsMonitor", method = "recordMsgSent")
+ public void atGrpcClientMsgSent(@This Object clientStats) {
+ GrpcClientStatsMonitor stats = (GrpcClientStatsMonitor) clientStats;
+ getGrpcClientStats(stats.getMethod()).incrementSentMsgCountBy(1);
+ }
+
+ private synchronized GrpcClientStats getGrpcClientStats(GrpcMethod method) {
+ String key = method.serviceName() + METRIC_KEY_DELIMETER + method.methodName();
+ GrpcClientStats stats = grpcClientCountByName.get(key);
+ if (stats == null) {
+ stats = new GrpcClientStats(method);
+ grpcClientCountByName.put(key, stats);
+ }
+ return stats;
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientStats.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientStats.java
new file mode 100644
index 00000000000..38166032f19
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientStats.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import com.ibm.websphere.monitor.meters.Counter;
+import com.ibm.websphere.monitor.meters.Meter;
+
+import io.openliberty.grpc.monitor.GrpcClientStatsMXBean;
+
+/**
+ * Holds metrics used for client-side monitoring of gRPC services.
+ * Statistic reported:
+ *
+ * - Total number of RPCs started on the client.
+ *
- Total number of RPCs completed on the client, regardless of success or
+ * failure.
+ *
- TODO Histogram of RPC response latency for completed RPCs, in seconds.
+ *
- Total number of stream messages received from the server.
+ *
- Total number of stream messages sent by the client.
+ *
+ */
+public class GrpcClientStats extends Meter implements GrpcClientStatsMXBean {
+ private final Counter rpcStarted;
+ private final Counter rpcCompleted;
+ private final Counter streamMessagesReceived;
+ private final Counter streamMessagesSent;
+
+ private final GrpcMethod method;
+
+ public GrpcClientStats(GrpcMethod method) {
+ this.method = method;
+
+ rpcStarted = new Counter();
+ rpcStarted.setDescription("This shows total number of RPCs started on the client");
+ rpcStarted.setUnit("ns");
+
+ rpcCompleted = new Counter();
+ rpcCompleted.setDescription("This shows total number of RPCs completed on the client");
+ rpcCompleted.setUnit("ns");
+
+ streamMessagesReceived = new Counter();
+ streamMessagesReceived.setDescription("This shows total number of stream messages received from the server");
+ streamMessagesReceived.setUnit("ns");
+
+ streamMessagesSent = new Counter();
+ streamMessagesSent.setDescription("This shows total number of stream messages sent by the client");
+ streamMessagesSent.setUnit("ns");
+ }
+
+ @Override
+ public long getRpcStartedCount() {
+ return rpcStarted.getCurrentValue();
+ }
+
+ @Override
+ public long getRpcCompletedCount() {
+ return rpcCompleted.getCurrentValue();
+ }
+
+ @Override
+ public long getReceivedMessagesCount() {
+ return streamMessagesReceived.getCurrentValue();
+ }
+
+ @Override
+ public long getSentMessagesCount() {
+ return streamMessagesSent.getCurrentValue();
+ }
+
+ public void recordCallStarted() {
+ rpcStarted.incrementBy(1);
+ }
+
+ public void recordClientHandled() {
+ rpcCompleted.incrementBy(1);
+ }
+
+ /**
+ * This will increment received messages count by the specified number.
+ *
+ * @param i
+ */
+ public void incrementReceivedMsgCountBy(int i) {
+ this.streamMessagesReceived.incrementBy(i);
+ }
+
+ /**
+ * This will increment sent messages count by the specified number.
+ *
+ * @param i
+ */
+ public void incrementSentMsgCountBy(int i) {
+ this.streamMessagesSent.incrementBy(i);
+ }
+
+ public void recordLatency(double latencySec) {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientStatsMonitor.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientStatsMonitor.java
new file mode 100644
index 00000000000..b7343f9d055
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcClientStatsMonitor.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+/**
+ * Holds metrics used for client-side monitoring of gRPC services.
+ * Statistic monitored:
+ *
+ * - Total number of RPCs started on the client.
+ *
- Total number of RPCs completed on the client, regardless of success or
+ * failure.
+ *
- TODO Histogram of RPC response latency for completed RPCs, in seconds.
+ *
- Total number of stream messages received from the server.
+ *
- Total number of stream messages sent by the client.
+ *
+ */
+public class GrpcClientStatsMonitor {
+
+ private final GrpcMethod method;
+
+ public GrpcClientStatsMonitor(GrpcMethod method) {
+ this.method = method;
+ }
+
+ public void recordCallStarted() {
+ }
+
+ public void recordClientHandled() {
+ }
+
+ public void recordMsgReceived() {
+ }
+
+ public void recordMsgSent() {
+ }
+
+ public void recordLatency(double latencySec) {
+ // TODO implement
+ }
+
+ public String getServiceName() {
+ return method.serviceName();
+ }
+
+ public GrpcMethod getMethod() {
+ return method;
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMethod.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMethod.java
new file mode 100644
index 00000000000..c6264baa5b2
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMethod.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import io.grpc.MethodDescriptor;
+import io.grpc.MethodDescriptor.MethodType;
+
+/** Extracts information about a single gRPC method. */
+class GrpcMethod {
+ private final String serviceName;
+ private final String methodName;
+ private final MethodType type;
+
+ static GrpcMethod of(MethodDescriptor, ?> method) {
+ String serviceName = MethodDescriptor.extractFullServiceName(method.getFullMethodName());
+
+ // full method names are of the form: "full.serviceName/MethodName". We extract
+ // the last part.
+ String methodName = method.getFullMethodName().substring(serviceName.length() + 1);
+ return new GrpcMethod(serviceName, methodName, method.getType());
+ }
+
+ private GrpcMethod(String serviceName, String methodName, MethodType type) {
+ this.serviceName = serviceName;
+ this.methodName = methodName;
+ this.type = type;
+ }
+
+ String serviceName() {
+ return serviceName;
+ }
+
+ String methodName() {
+ return methodName;
+ }
+
+ String type() {
+ return type.toString();
+ }
+
+ boolean serverSendsOneMessage() {
+ return type.serverSendsOneMessage();
+ }
+
+ boolean clientSendsOneMessage() {
+ return type.clientSendsOneMessage();
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientCall.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientCall.java
new file mode 100644
index 00000000000..45c02be57e5
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientCall.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import java.time.Clock;
+
+import io.grpc.ClientCall;
+import io.grpc.ForwardingClientCall;
+import io.grpc.Metadata;
+
+/**
+ * A {@link SimpleForwardingClientCall} which increments counters for the RPC
+ * call.
+ */
+public class GrpcMonitoringClientCall
+ extends ForwardingClientCall.SimpleForwardingClientCall {
+ private final GrpcClientStatsMonitor clientMetrics;
+ private final GrpcMethod grpcMethod;
+ private final Clock clock;
+
+ protected GrpcMonitoringClientCall(ClientCall delegate, GrpcClientStatsMonitor clientMetrics,
+ GrpcMethod grpcMethod, Clock clock) {
+ super(delegate);
+ this.clientMetrics = clientMetrics;
+ this.grpcMethod = grpcMethod;
+ this.clock = clock;
+ }
+
+ @Override
+ public void start(ClientCall.Listener delegate, Metadata metadata) {
+ clientMetrics.recordCallStarted();
+ super.start(new GrpcMonitoringClientCallListener<>(delegate, clientMetrics, grpcMethod, clock),
+ metadata);
+ }
+
+ @Override
+ public void sendMessage(ReqT requestMessage) {
+ if (grpcMethod.serverSendsOneMessage()) {
+ clientMetrics.recordMsgSent();;
+ }
+ super.sendMessage(requestMessage);
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientCallListener.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientCallListener.java
new file mode 100644
index 00000000000..362a5b3c8d6
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientCallListener.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import java.time.Clock;
+import java.time.Instant;
+
+import io.grpc.ClientCall;
+import io.grpc.ClientCall.Listener;
+import io.grpc.ForwardingClientCallListener;
+import io.grpc.Metadata;
+import io.grpc.Status;
+
+public class GrpcMonitoringClientCallListener extends ForwardingClientCallListener {
+ private static final long MILLIS_PER_SECOND = 1000L;
+
+ private final ClientCall.Listener delegate;
+ private final GrpcClientStatsMonitor clientMetrics;
+ private final GrpcMethod grpcMethod;
+ private final Clock clock;
+ private final Instant startInstant;
+
+ GrpcMonitoringClientCallListener(ClientCall.Listener delegate, GrpcClientStatsMonitor clientMetrics,
+ GrpcMethod grpcMethod, Clock clock) {
+ this.delegate = delegate;
+ this.clientMetrics = clientMetrics;
+ this.grpcMethod = grpcMethod;
+ this.clock = clock;
+ this.startInstant = clock.instant();
+ }
+
+ @Override
+ protected Listener delegate() {
+ return delegate;
+ }
+
+ @Override
+ public void onClose(Status status, Metadata metadata) {
+ clientMetrics.recordClientHandled();// status.getCode());
+ double latencySec = (clock.millis() - startInstant.toEpochMilli()) / (double) MILLIS_PER_SECOND;
+ clientMetrics.recordLatency(latencySec);
+ super.onClose(status, metadata);
+ }
+
+ @Override
+ public void onMessage(RespT responseMessage) {
+ if (grpcMethod.clientSendsOneMessage()) {
+ clientMetrics.recordMsgReceived();
+ }
+ super.onMessage(responseMessage);
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientInterceptor.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientInterceptor.java
new file mode 100644
index 00000000000..520c4da53a7
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringClientInterceptor.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import java.time.Clock;
+
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.ClientInterceptor;
+import io.grpc.MethodDescriptor;
+
+/**
+ * A {@link ClientInterceptor} which gathers statistics about incoming GRPC
+ * calls.
+ */
+public class GrpcMonitoringClientInterceptor implements ClientInterceptor {
+ private final Clock clock;
+
+ public GrpcMonitoringClientInterceptor() {
+ this.clock = Clock.systemDefaultZone();
+ }
+
+ @Override
+ public ClientCall interceptCall(MethodDescriptor methodDescriptor,
+ CallOptions callOptions, Channel channel) {
+ GrpcMethod grpcMethod = GrpcMethod.of(methodDescriptor);
+ GrpcClientStatsMonitor metrics = new GrpcClientStatsMonitor(grpcMethod);
+ return new GrpcMonitoringClientCall<>(channel.newCall(methodDescriptor, callOptions), metrics, grpcMethod,
+ clock);
+ }
+
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerCall.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerCall.java
new file mode 100644
index 00000000000..cd0a4bdca0f
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerCall.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import java.time.Clock;
+import java.time.Instant;
+
+import io.grpc.ForwardingServerCall;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.Status;
+
+/**
+ * A {@link ForwardingServerCall} which updates gRPC metrics based on the
+ * server-side actions taken for a single RPC, e.g., messages sent, latency,
+ * etc.
+ */
+public class GrpcMonitoringServerCall extends ForwardingServerCall.SimpleForwardingServerCall {
+ private static final long MILLIS_PER_SECOND = 1000L;
+
+ private final Clock clock;
+ private final GrpcMethod grpcMethod;
+ private final GrpcServerStatsMonitor serverMetrics;
+ private final Instant startInstant;
+
+ GrpcMonitoringServerCall(ServerCall delegate, Clock clock, GrpcMethod grpcMethod, GrpcServerStatsMonitor serverMetrics) {
+ super(delegate);
+ this.clock = clock;
+ this.grpcMethod = grpcMethod;
+ this.serverMetrics = serverMetrics;
+ this.startInstant = clock.instant();
+
+ reportStartMetrics();
+ }
+
+ @Override
+ public void close(Status status, Metadata responseHeaders) {
+ reportEndMetrics(status);
+ super.close(status, responseHeaders);
+ }
+
+ @Override
+ public void sendMessage(S message) {
+ if (grpcMethod.clientSendsOneMessage()) {
+ serverMetrics.recordMsgSent();
+ }
+ super.sendMessage(message);
+ }
+
+ private void reportStartMetrics() {
+ serverMetrics.recordCallStarted();
+ }
+
+ private void reportEndMetrics(Status status) {
+ serverMetrics.recordServerHandled();//(status.getCode());
+ double latencySec = (clock.millis() - startInstant.toEpochMilli()) / (double) MILLIS_PER_SECOND;
+ serverMetrics.recordLatency(latencySec);
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerCallListener.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerCallListener.java
new file mode 100644
index 00000000000..3c747e1fe80
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerCallListener.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import io.grpc.ForwardingServerCallListener;
+import io.grpc.ServerCall;
+
+/**
+ * A {@link ForwardingServerCallListener} which updates gRPC metrics for a
+ * single gRPC service based on updates received from gRPC.
+ */
+class GrpcMonitoringServerCallListener extends ForwardingServerCallListener {
+ private final ServerCall.Listener delegate;
+ private final GrpcMethod grpcMethod;
+ private final GrpcServerStatsMonitor serverMetrics;
+
+ GrpcMonitoringServerCallListener(ServerCall.Listener delegate, GrpcServerStatsMonitor serverMetrics,
+ GrpcMethod grpcMethod) {
+ this.delegate = delegate;
+ this.serverMetrics = serverMetrics;
+ this.grpcMethod = grpcMethod;
+ }
+
+ @Override
+ protected ServerCall.Listener delegate() {
+ return delegate;
+ }
+
+ @Override
+ public void onMessage(R request) {
+ if (grpcMethod.serverSendsOneMessage()) {
+ serverMetrics.recordMsgReceived();
+ }
+ super.onMessage(request);
+ }
+}
\ No newline at end of file
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerInterceptor.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerInterceptor.java
new file mode 100644
index 00000000000..160d7637d72
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcMonitoringServerInterceptor.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import java.time.Clock;
+
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+import io.grpc.ServerCall;
+import io.grpc.ServerCall.Listener;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+
+/**
+ * A {@link ServerInterceptor} which gathers statistics about incoming GRPC
+ * calls.
+ */
+public class GrpcMonitoringServerInterceptor implements ServerInterceptor {
+ private String serviceName;
+ private String appName;
+ private final Clock clock;
+
+ public GrpcMonitoringServerInterceptor(String serviceName, String appName) {
+ this.serviceName = serviceName;
+ this.appName = appName;
+ this.clock = Clock.systemDefaultZone();
+ }
+
+ @Override
+ public Listener interceptCall(ServerCall call, Metadata headers,
+ ServerCallHandler next) {
+ MethodDescriptor methodDescriptor = call.getMethodDescriptor();
+ GrpcMethod grpcMethod = GrpcMethod.of(methodDescriptor);
+ GrpcServerStatsMonitor metrics = new GrpcServerStatsMonitor(appName, serviceName);
+ ServerCall monitoringCall = new GrpcMonitoringServerCall(call, clock, grpcMethod, metrics);
+ return new GrpcMonitoringServerCallListener<>(next.startCall(monitoringCall, headers), metrics, grpcMethod);
+ }
+
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerMonitor.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerMonitor.java
new file mode 100644
index 00000000000..b6353d8323c
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerMonitor.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import com.ibm.websphere.monitor.annotation.Monitor;
+import com.ibm.websphere.monitor.annotation.ProbeAtEntry;
+import com.ibm.websphere.monitor.annotation.ProbeAtReturn;
+import com.ibm.websphere.monitor.annotation.ProbeSite;
+import com.ibm.websphere.monitor.annotation.PublishedMetric;
+import com.ibm.websphere.monitor.annotation.This;
+import com.ibm.websphere.monitor.meters.MeterCollection;
+
+/**
+ * Monitor class for gRPC server.
+ * This class is responsible for managing the gRPC server MXBean object, as well
+ * as the actual updating of the values of the counters defined in the MXBean
+ * object.
+ */
+@Monitor(group = "GrpcServer")
+public class GrpcServerMonitor {
+
+ @PublishedMetric
+ public MeterCollection grpcServerCountByName = new MeterCollection(
+ "GrpcServer", this);
+
+ @ProbeAtEntry
+ @ProbeSite(clazz = "io.openliberty.grpc.internal.monitor.GrpcServerStatsMonitor", method = "recordCallStarted")
+ public void atGrpcServerStart(@This Object serverStats) {
+ GrpcServerStatsMonitor stats = (GrpcServerStatsMonitor) serverStats;
+ getGrpcServerStats(stats.getAppName(), stats.getServiceName()).recordCallStarted();
+ }
+
+ @ProbeAtEntry
+ @ProbeSite(clazz = "io.openliberty.grpc.internal.monitor.GrpcServerStatsMonitor", method = "recordServerHandled")
+ public void atGrpcServerHandled(@This Object serverStats) {
+ GrpcServerStatsMonitor stats = (GrpcServerStatsMonitor) serverStats;
+ getGrpcServerStats(stats.getAppName(), stats.getServiceName()).recordServerHandled();
+ }
+
+ @ProbeAtReturn
+ @ProbeSite(clazz = "io.openliberty.grpc.internal.monitor.GrpcServerStatsMonitor", method = "recordMsgReceived")
+ public void atGrpcServerMsgReceived(@This Object serverStats) {
+ GrpcServerStatsMonitor stats = (GrpcServerStatsMonitor) serverStats;
+ getGrpcServerStats(stats.getAppName(), stats.getServiceName()).incrementReceivedMsgCountBy(1);
+ }
+
+ @ProbeAtReturn
+ @ProbeSite(clazz = "io.openliberty.grpc.internal.monitor.GrpcServerStatsMonitor", method = "recordMsgSent")
+ public void atGrpcServerMsgSent(@This Object serverStats) {
+ GrpcServerStatsMonitor stats = (GrpcServerStatsMonitor) serverStats;
+ getGrpcServerStats(stats.getAppName(), stats.getServiceName()).incrementSentMsgCountBy(1);
+ }
+
+ private synchronized GrpcServerStats getGrpcServerStats(String appName, String serviceName) {
+ GrpcServerStats stats = grpcServerCountByName.get(serviceName);
+ if (stats == null) {
+ stats = new GrpcServerStats(appName, serviceName);
+ grpcServerCountByName.put(serviceName, stats);
+ }
+ return stats;
+ }
+
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerStats.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerStats.java
new file mode 100644
index 00000000000..80acfec1cfc
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerStats.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+import com.ibm.websphere.monitor.meters.Counter;
+import com.ibm.websphere.monitor.meters.Meter;
+
+import io.openliberty.grpc.monitor.GrpcServerStatsMXBean;
+
+/**
+ * This is used to report gRPC Server related statistics.
+ * Statistic reported:
+ *
+ * - Total number of RPCs started on the server.
+ *
- Total number of RPCs completed on the server, regardless of success or
+ * failure.
+ *
- TODO Histogram of response latency of RPCs handled by the server, in
+ * seconds.
+ *
- Total number of stream messages received from the client.
+ *
- Total number of stream messages sent by the server.
+ *
+ */
+public class GrpcServerStats extends Meter implements GrpcServerStatsMXBean {
+
+ private String appName;
+ private String serviceName;
+
+ private Counter receivedMsgCount;
+ private Counter sentMsgCount;
+ private Counter serverStarted;
+ private Counter serverHandled;
+
+ public GrpcServerStats(String aName, String sName) {
+ setAppName(aName);
+ setServiceName(sName);
+
+ serverStarted = new Counter();
+ serverStarted.setDescription("This shows total number of RPCs started on the server");
+ serverStarted.setUnit("ns");
+
+ serverHandled = new Counter();
+ serverHandled.setDescription("This shows total number of RPCs completed on the server");
+ serverHandled.setUnit("ns");
+
+ receivedMsgCount = new Counter();
+ receivedMsgCount.setDescription("This shows number of received stream messages");
+ receivedMsgCount.setUnit("ns");
+
+ sentMsgCount = new Counter();
+ sentMsgCount.setDescription("This shows number of stream messages sent by the service");
+ sentMsgCount.setUnit("ns");
+ }
+
+ @Override
+ public String getAppName() {
+ return this.appName;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Report gRPC statistics for the specified service and application.";
+ }
+
+ /**
+ * @param appName the application name which contains the specified gRPC service
+ */
+ public void setAppName(String appName) {
+ this.appName = appName;
+ }
+
+ /**
+ * @param name the servletName to set
+ */
+ public void setServiceName(String name) {
+ this.serviceName = name;
+ }
+
+ public void recordCallStarted() {
+ serverStarted.incrementBy(1);
+ }
+
+ public void recordServerHandled() {
+ serverHandled.incrementBy(1);
+ }
+
+ /**
+ * This will increment received messages count by the specified number.
+ *
+ * @param i
+ */
+ public void incrementReceivedMsgCountBy(int i) {
+ this.receivedMsgCount.incrementBy(i);
+ }
+
+ /**
+ * This will increment sent messages count by the specified number.
+ *
+ * @param i
+ */
+ public void incrementSentMsgCountBy(int i) {
+ this.sentMsgCount.incrementBy(i);
+ }
+
+ public void recordLatency(double latencySec) {
+ // TODO implement
+ }
+
+ @Override
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ @Override
+ public long getReceivedMessagesCount() {
+ return receivedMsgCount.getCurrentValue();
+ }
+
+ @Override
+ public long getSentMessagesCount() {
+ return sentMsgCount.getCurrentValue();
+ }
+
+ @Override
+ public long getRpcStartedCount() {
+ return serverStarted.getCurrentValue();
+ }
+
+ @Override
+ public long getRpcCompletedCount() {
+ return serverHandled.getCurrentValue();
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerStatsMonitor.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerStatsMonitor.java
new file mode 100644
index 00000000000..5c44bcd6e09
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/GrpcServerStatsMonitor.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.internal.monitor;
+
+/**
+ * This is used to monitor gRPC Server related statistics.
+ * Statistic monitored:
+ *
+ * - Total number of RPCs started on the server.
+ *
- Total number of RPCs completed on the server, regardless of success or
+ * failure.
+ *
- TODO Histogram of response latency of RPCs handled by the server, in
+ * seconds.
+ *
- Total number of stream messages received from the client.
+ *
- Total number of stream messages sent by the server.
+ *
+ */
+public class GrpcServerStatsMonitor {
+
+ private String appName;
+ private String serviceName;
+
+ public GrpcServerStatsMonitor(String aName, String sName) {
+ setAppName(aName);
+ setServiceName(sName);
+ }
+
+ public String getAppName() {
+ return this.appName;
+ }
+
+ /**
+ * @param appName the application name which contains the specified gRPC service
+ */
+ public void setAppName(String appName) {
+ this.appName = appName;
+ }
+
+ /**
+ * @param name the servletName to set
+ */
+ public void setServiceName(String name) {
+ this.serviceName = name;
+ }
+
+ public void recordCallStarted() {
+ }
+
+ public void recordServerHandled() {
+ }
+
+ /**
+ * This will increment received messages count
+ */
+ public void recordMsgReceived() {
+ }
+
+ /**
+ * This will increment sent messages count
+ */
+ public void recordMsgSent() {
+ }
+
+ public void recordLatency(double latencySec) {
+ // TODO implement
+ }
+
+ public String getServiceName() {
+ return serviceName;
+ }
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/package-info.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/package-info.java
new file mode 100644
index 00000000000..59919033917
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/internal/monitor/package-info.java
@@ -0,0 +1,12 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+@org.osgi.annotation.versioning.Version("1.0")
+package io.openliberty.grpc.internal.monitor;
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/GrpcClientStatsMXBean.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/GrpcClientStatsMXBean.java
new file mode 100644
index 00000000000..b8c546123a9
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/GrpcClientStatsMXBean.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.monitor;
+
+/**
+ * Management interface for MBeans with names of the form "WebSphere:type=GrpcClientStats,name=*"
+ * where * is the name of a gRPC method of the form /.
+ *
+ * @ibm-api
+ */
+public interface GrpcClientStatsMXBean {
+ /**
+ * Retrieves the total number of RPCs started on the client.
+ *
+ * @return the total number of RPCs started on the client
+ */
+ public long getRpcStartedCount();
+
+ /**
+ * Retrieves the total number of RPCs completed on the client,
+ *
+ * @return the total number of RPCs completed on the client
+ */
+ public long getRpcCompletedCount();
+
+ /**
+ * Retrieves the total number of stream messages that the client has received from the gRPC server.
+ *
+ * @return the total number of stream messages received from the server
+ */
+ public long getReceivedMessagesCount();
+
+ /**
+ * Retrieves the total number of stream messages sent by the client.
+ *
+ * @return the total number of stream messages sent by the client
+ */
+ public long getSentMessagesCount();
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/GrpcServerStatsMXBean.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/GrpcServerStatsMXBean.java
new file mode 100644
index 00000000000..78691fa3342
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/GrpcServerStatsMXBean.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package io.openliberty.grpc.monitor;
+
+/**
+ * Management interface for MBeans with names of the form "WebSphere:type=GrpcServerStats,name=*"
+ * where * is the name of a gRPC service.
+ *
+ * @ibm-api
+ */
+public interface GrpcServerStatsMXBean {
+
+ /**
+ * Retrieves the gRPC service name.
+ *
+ * @return the gRPC service name
+ */
+ public String getServiceName();
+
+ /**
+ * Retrieves the total number of RPCs started on the server.
+ *
+ * @return the total number of RPCs started on the server
+ */
+ public long getRpcStartedCount();
+
+ /**
+ * Retrieves the total number of RPCs completed on the server,
+ *
+ * @return the total number of RPCs completed on the server
+ */
+ public long getRpcCompletedCount();
+
+ /**
+ * Retrieves the total number of stream messages that the server has received for this gRPC service.
+ *
+ * @return the total number of stream messages received for this service
+ */
+ public long getReceivedMessagesCount();
+
+ /**
+ * Retrieves the total number of stream messages sent by this gRPC service.
+ *
+ * @return the total number of stream messages sent by this service
+ */
+ public long getSentMessagesCount();
+
+ /**
+ * Retrieves the name of the application of which the gRPC service is a member.
+ *
+ * @return application name
+ */
+ public String getAppName();
+}
diff --git a/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/package-info.java b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/package-info.java
new file mode 100644
index 00000000000..6582559ce4d
--- /dev/null
+++ b/dev/io.openliberty.grpc.1.0.internal.monitor/src/io/openliberty/grpc/monitor/package-info.java
@@ -0,0 +1,15 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/**
+ * @version 1.1
+ */
+@org.osgi.annotation.versioning.Version("1.1")
+package io.openliberty.grpc.monitor;
diff --git a/dev/io.openliberty.grpc.1.0.internal/bnd.bnd b/dev/io.openliberty.grpc.1.0.internal/bnd.bnd
index 344c9d62462..812ee9390cc 100644
--- a/dev/io.openliberty.grpc.1.0.internal/bnd.bnd
+++ b/dev/io.openliberty.grpc.1.0.internal/bnd.bnd
@@ -27,7 +27,15 @@ Import-Package: !sun.*,\
!com.google.code.gson,\
!org.checkerframework,\
javax.annotation;version=!,\
+ !com.ibm.websphere.monitor.jmx,\
*
+
+# Use dynamic import of monitor packages to support indepndent/dynamic enablement of monitor feature
+DynamicImport-Package: com.ibm.websphere.monitor.meters;version="1.0.0", \
+ com.ibm.websphere.monitor.jmx;version="1.0.0", \
+ com.ibm.wsspi.request.probe.bci, \
+ com.ibm.wsspi.probeExtension, \
+ io.openliberty.grpc.internal.monitor
Export-Package: \
io.grpc.servlet,\
@@ -45,6 +53,7 @@ Include-Resource: \
com.ibm.websphere.javaee.servlet.4.0;version=latest,\
com.ibm.websphere.org.osgi.core;version=latest,\
com.ibm.websphere.org.osgi.service.component;version=latest,\
+ com.ibm.ws.monitor;version=latest,\
com.ibm.ws.org.osgi.annotation.versioning;version=latest,\
com.ibm.wsspi.org.osgi.service.component.annotations;version=latest,\
com.ibm.ws.injection.core;version=latest,\
diff --git a/dev/io.openliberty.grpc.1.0.internal/src/io/grpc/servlet/GrpcServlet.java b/dev/io.openliberty.grpc.1.0.internal/src/io/grpc/servlet/GrpcServlet.java
index 5703c23134e..0976809b49b 100644
--- a/dev/io.openliberty.grpc.1.0.internal/src/io/grpc/servlet/GrpcServlet.java
+++ b/dev/io.openliberty.grpc.1.0.internal/src/io/grpc/servlet/GrpcServlet.java
@@ -16,6 +16,13 @@
package io.grpc.servlet;
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
import com.google.common.annotations.VisibleForTesting;
import com.ibm.websphere.ras.annotation.Trivial;
@@ -23,12 +30,6 @@
import io.grpc.ExperimentalApi;
import io.openliberty.grpc.internal.servlet.GrpcServletUtils;
-import java.io.IOException;
-import java.util.List;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
/**
* A simple servlet backed by a gRPC server. Must set {@code asyncSupported} to true. The {@code
* /contextRoot/urlPattern} must match the gRPC services' path, which is
@@ -55,13 +56,13 @@ public class GrpcServlet extends HttpServlet {
* added on each gRPC service by {@link
* io.grpc.ServerInterceptors#intercept(BindableService, io.grpc.ServerInterceptor...)}
*/
- public GrpcServlet(List extends BindableService> bindableServices) {
- this(loadServices(bindableServices));
+ public GrpcServlet(List extends BindableService> bindableServices, String appName) {
+ this(loadServices(bindableServices, appName));
}
- private static ServletAdapter loadServices(List extends BindableService> bindableServices) {
+ private static ServletAdapter loadServices(List extends BindableService> bindableServices, String appName) {
ServletServerBuilder serverBuilder = new ServletServerBuilder();
- GrpcServletUtils.addServices(bindableServices, serverBuilder);
+ GrpcServletUtils.addServices(bindableServices, serverBuilder, appName);
return serverBuilder.buildServletAdapter();
}
diff --git a/dev/io.openliberty.grpc.1.0.internal/src/io/openliberty/grpc/internal/servlet/GrpcServerComponent.java b/dev/io.openliberty.grpc.1.0.internal/src/io/openliberty/grpc/internal/servlet/GrpcServerComponent.java
index 4bbd5eabd96..dab174c5ab6 100644
--- a/dev/io.openliberty.grpc.1.0.internal/src/io/openliberty/grpc/internal/servlet/GrpcServerComponent.java
+++ b/dev/io.openliberty.grpc.1.0.internal/src/io/openliberty/grpc/internal/servlet/GrpcServerComponent.java
@@ -64,6 +64,8 @@ public class GrpcServerComponent implements ServletContainerInitializer, Applica
};
private static boolean useSecurity = false;
+ /** Indicates whether the monitor feature is enabled */
+ private static boolean monitoringEnabled = false;
private final String FEATUREPROVISIONER_REFERENCE_NAME = "featureProvisioner";
@@ -124,7 +126,7 @@ public void onStartup(Set> ctx, ServletContext sc) throws ServletExcept
// pass all of our grpc service implementors into a new GrpcServlet
// and register that new Servlet on this context
GrpcServlet grpcServlet = new GrpcServlet(
- new ArrayList(grpcServiceClasses.values()));
+ new ArrayList(grpcServiceClasses.values()), ((WebApp) sc).getApplicationName());
ServletRegistration.Dynamic servletRegistration = sc.addServlet("grpcServlet" + ":" + serviceName, grpcServlet);
servletRegistration.setAsyncSupported(true);
@@ -213,6 +215,7 @@ private void initGrpcServices(ApplicationInfo appInfo) {
@Override
public void applicationStarting(ApplicationInfo appInfo) throws StateChangeException {
setSecurityEnabled();
+ setMonitoringEnabled();
initGrpcServices(appInfo);
}
@@ -253,4 +256,20 @@ private void setSecurityEnabled() {
public static boolean isSecurityEnabled() {
return useSecurity;
}
+
+ /**
+ * Set the indication whether the monitor feature is enabled
+ */
+ private void setMonitoringEnabled() {
+ Set currentFeatureSet = _featureProvisioner.getService().getInstalledFeatures();
+ monitoringEnabled = currentFeatureSet.contains("monitor-1.0");
+ }
+
+ /**
+ * @return true
if the monitor feature is enabled,
+ * false
otherwise
+ */
+ public static boolean isMonitoringEnabled() {
+ return monitoringEnabled;
+ }
}
diff --git a/dev/io.openliberty.grpc.1.0.internal/src/io/openliberty/grpc/internal/servlet/GrpcServletUtils.java b/dev/io.openliberty.grpc.1.0.internal/src/io/openliberty/grpc/internal/servlet/GrpcServletUtils.java
index dbc661f283b..38da38bbd49 100644
--- a/dev/io.openliberty.grpc.1.0.internal/src/io/openliberty/grpc/internal/servlet/GrpcServletUtils.java
+++ b/dev/io.openliberty.grpc.1.0.internal/src/io/openliberty/grpc/internal/servlet/GrpcServletUtils.java
@@ -233,24 +233,52 @@ public static List getUserInterceptors(String service) {
* @param serverBuilder
*/
public static void addServices(List extends BindableService> bindableServices,
- ServletServerBuilder serverBuilder) {
+ ServletServerBuilder serverBuilder, String appName) {
for (BindableService service : bindableServices) {
- String name = service.bindService().getServiceDescriptor().getName();
+ String serviceName = service.bindService().getServiceDescriptor().getName();
// set any user-defined server interceptors and add the service
- List interceptors = GrpcServletUtils.getUserInterceptors(name);
+ List interceptors = GrpcServletUtils.getUserInterceptors(serviceName);
// add Liberty auth interceptor to every service
interceptors.add(authInterceptor);
+ // add monitoring interceptor to every service
+ ServerInterceptor monitoringInterceptor = createMonitoringServerInterceptor(serviceName, appName);
+ if (monitoringInterceptor != null) {
+ interceptors.add(monitoringInterceptor);
+ }
serverBuilder.addService(ServerInterceptors.intercept(service, interceptors));
// set the max inbound msg size, if it's configured
- int maxInboundMsgSize = GrpcServiceConfigHolder.getMaxInboundMessageSize(name);
+ int maxInboundMsgSize = GrpcServiceConfigHolder.getMaxInboundMessageSize(serviceName);
if (maxInboundMsgSize != -1) {
serverBuilder.maxInboundMessageSize(maxInboundMsgSize);
}
if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
- Tr.debug(tc, "gRPC service {0} has been registered", name);
+ Tr.debug(tc, "gRPC service {0} has been registered", serviceName);
}
}
}
+
+
+ private static ServerInterceptor createMonitoringServerInterceptor(String serviceName, String appName) {
+ // create the monitoring interceptor only if the monitor feature is enabled
+ if (!GrpcServerComponent.isMonitoringEnabled()) {
+ return null;
+ }
+ ServerInterceptor interceptor = null;
+ // monitoring interceptor
+ final String className = "io.openliberty.grpc.internal.monitor.GrpcMonitoringServerInterceptor";
+ try {
+ Class> clazz = Class.forName(className);
+ interceptor = (ServerInterceptor) clazz.getDeclaredConstructor(String.class, String.class)
+ .newInstance(serviceName, appName);
+ if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
+ Tr.debug(tc, "monitoring interceptor has been added to service {0}", serviceName);
+ }
+ } catch (Exception e) {
+ // an exception can happen if the monitoring package is not loaded
+ }
+
+ return interceptor;
+ }
}