diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml
index e19c1de3a7..513f9e8df5 100644
--- a/.github/workflows/e2e.yaml
+++ b/.github/workflows/e2e.yaml
@@ -116,6 +116,8 @@ jobs:
./mvnw --batch-mode -f test/e2e/pom.xml -pl e2e-base clean install
- name: 6.x Agents & 7.x Backend
run: export E2E_VERSION=jdk8-1.5 && bash -x test/e2e/run.sh e2e-6.x-agent-7.x-oap-compatibility
+ - name: Http API with Nginx Lua Tests In Single Node Mode(JDK8)
+ run: export E2E_VERSION=jdk8-1.5 && bash -x test/e2e/run.sh e2e-http-api-with-nginx-lua
Profile:
runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index c8c84886d0..a5bd42a680 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ OALLexer.tokens
.externalToolBuilders
/test/plugin/dist
/test/plugin/workspace
+e2e-*
diff --git a/docs/en/protocols/HTTP-API-Protocol.md b/docs/en/protocols/HTTP-API-Protocol.md
new file mode 100644
index 0000000000..3b42a4223b
--- /dev/null
+++ b/docs/en/protocols/HTTP-API-Protocol.md
@@ -0,0 +1,200 @@
+# HTTP API Protocol
+
+HTTP API Protocol defines the API data format, including api request and response data format.
+
+### Do register
+
+Detail information about data format can be found in [Register service](https://github.com/apache/skywalking-data-collect-protocol/tree/master/register/Register.proto).
+And register steps followings [SkyWalking Trace Data Protocol v2](Trace-Data-Protocol-v2.md).
+
+- Service Register
+
+> POST http://localhost:12800/v2/service/register
+
+Input:
+
+```json
+{
+ "services": [
+ {
+ "type": "normal",
+ "serviceName": "Service Name"
+ }
+ ]
+}
+```
+
+Output JSON Array:
+
+```json
+[
+ {
+ "key": "Service Name",
+ "value": 2
+ }
+]
+```
+
+- Service instance Register
+
+> POST http://localhost:12800/v2/instance/register
+
+Input:
+
+```json
+{
+ "instances": [
+ {
+ "time": 1582428603392,
+ "instanceUUID": "NAME:Service Instance Name",
+ "properties": [
+ {
+ "key": "language",
+ "value": "Lua"
+ }
+ ],
+ "serviceId": 2
+ }
+ ]
+}
+```
+
+OutPut:
+
+```json
+[
+ {
+ "key": "NAME:Service Instance Name",
+ "value": 0
+ }
+]
+```
+
+- Service instance heartbeat
+
+> POST http://localhost:12800/v2/instance/heartbeat
+
+Input:
+
+```json
+{
+ "serviceInstanceId":20,
+ "time": 1582428603392,
+ "serviceInstanceUUID":"NAME:Service Instance Name"
+}
+```
+
+OutPut:
+
+```json
+{}
+```
+If your instance does not exist, you need to clean your local service instance metadata in your application and re-do register:
+
+```json
+{
+ "commands": [
+ {
+ "command": "ServiceMetadataReset",
+ "args": [
+ {
+ "key": "SerialNumber",
+ "value": "44bd2664-03c7-46bc-8652-52fcde0e7699"
+ }
+ ]
+ }
+ ]
+}
+```
+
+## Trace Report
+
+### POST http://localhost:12800/v2/segments
+
+Input:
+
+```json
+{
+ "spans": [
+ {
+ "operationName": "/tier2/lb",
+ "startTime": 1582461179910,
+ "tags": [],
+ "endTime": 1582461179922,
+ "spanType": "Exit",
+ "logs":[],
+ "spanId": 1,
+ "isError": false,
+ "parentSpanId": 0,
+ "componentId": 6000,
+ "peer": "User Service Name-nginx:upstream_ip:port",
+ "spanLayer": "HTTP"
+ },
+ {
+ "operationName": "/tier2/lb",
+ "startTime": 1582461179910,
+ "tags": [
+ {
+ "key": "http.method",
+ "value": "GET"
+ },
+ {
+ "key": "http.params",
+ "value": "http://127.0.0.1/tier2/lb"
+ }
+ ],
+ "endTime": 1582461179922,
+ "spanType": "Entry",
+ "logs": [],
+ "spanId": 0,
+ "isError": false,
+ "parentSpanId": -1,
+ "componentId": 6000,
+ "refs": [
+ {
+ "parentTraceSegmentId": {
+ "idParts": [
+ 1582461179038,
+ 794206293,
+ 69887
+ ]
+ },
+ "parentEndpointId": 0,
+ "entryEndpointId": 0,
+ "parentServiceInstanceId": 1,
+ "parentEndpoint": "/ingress",
+ "networkAddress": "#User Service Name-nginx:upstream_ip:port",
+ "parentSpanId": 1,
+ "entryServiceInstanceId": 1,
+ "networkAddressId": 0,
+ "entryEndpoint": "/ingress"
+ }
+ ],
+ "spanLayer": "HTTP"
+ }
+ ],
+ "serviceInstanceId": 1,
+ "serviceId": 1,
+ "traceSegmentId": {
+ "idParts": [
+ 1582461179044,
+ 794206293,
+ 69887
+ ]
+ },
+ "globalTraceIds": [
+ {
+ "idParts": [
+ 1582461179038,
+ 794206293,
+ 69887
+ ]
+ }
+ ]
+}
+```
+ OutPut:
+
+ ```json
+
+```
\ No newline at end of file
diff --git a/docs/en/protocols/Trace-Data-Protocol-v2.md b/docs/en/protocols/Trace-Data-Protocol-v2.md
index a80a8404c2..26079bc4d7 100644
--- a/docs/en/protocols/Trace-Data-Protocol-v2.md
+++ b/docs/en/protocols/Trace-Data-Protocol-v2.md
@@ -7,6 +7,9 @@ Trace data protocol is defined and provided in [gRPC format](https://github.com/
For each agent/SDK, it needs to register service id and service instance id before reporting any kind of trace
or metrics data.
+Since SkyWalking v7.x, SkyWalking provided register and uplink trace data through HTTP API way.
+[HTTP API Protocol](HTTP-API-Protocol.md) defined the API data format.
+
### Step 1. Do register
[Register service](https://github.com/apache/skywalking-data-collect-protocol/tree/master/register/Register.proto) takes charge of
all register methods. At step 1, we need `doServiceRegister`, then `doServiceInstanceRegister`.
diff --git a/oap-server/pom.xml b/oap-server/pom.xml
index a90031f8e7..88562cb4db 100755
--- a/oap-server/pom.xml
+++ b/oap-server/pom.xml
@@ -90,6 +90,7 @@
3.25.0-GA3.5
+ 3.11.4
@@ -263,6 +264,13 @@
grpc-protobuf${grpc.version}
+
+
+ com.google.protobuf
+ protobuf-java-util
+ ${protobuf-java-util.version}
+
+
io.grpcgrpc-stub
diff --git a/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/jetty/JettyJsonHandler.java b/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/jetty/JettyJsonHandler.java
index f2bbdedcc8..b48a3248e6 100644
--- a/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/jetty/JettyJsonHandler.java
+++ b/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/jetty/JettyJsonHandler.java
@@ -19,6 +19,7 @@
package org.apache.skywalking.oap.server.library.server.jetty;
import com.google.gson.JsonElement;
+import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
@@ -84,25 +85,25 @@ public abstract class JettyJsonHandler extends JettyHandler {
@Override
protected final void doDelete(HttpServletRequest req,
- HttpServletResponse resp) throws ServletException, IOException {
+ HttpServletResponse resp) throws ServletException, IOException {
super.doDelete(req, resp);
}
@Override
protected final void doOptions(HttpServletRequest req,
- HttpServletResponse resp) throws ServletException, IOException {
+ HttpServletResponse resp) throws ServletException, IOException {
super.doOptions(req, resp);
}
@Override
protected final void doTrace(HttpServletRequest req,
- HttpServletResponse resp) throws ServletException, IOException {
+ HttpServletResponse resp) throws ServletException, IOException {
super.doTrace(req, resp);
}
@Override
protected final void service(HttpServletRequest req,
- HttpServletResponse resp) throws ServletException, IOException {
+ HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
}
@@ -189,4 +190,14 @@ public abstract class JettyJsonHandler extends JettyHandler {
out.flush();
out.close();
}
+
+ public String getJsonBody(HttpServletRequest req) throws IOException {
+ StringBuffer stringBuffer = new StringBuffer();
+ String line = null;
+ BufferedReader reader = req.getReader();
+ while ((line = reader.readLine()) != null) {
+ stringBuffer.append(line);
+ }
+ return stringBuffer.toString();
+ }
}
diff --git a/oap-server/server-library/library-util/pom.xml b/oap-server/server-library/library-util/pom.xml
index 93b2fa78e8..6f5b29a4b1 100644
--- a/oap-server/server-library/library-util/pom.xml
+++ b/oap-server/server-library/library-util/pom.xml
@@ -31,6 +31,7 @@
1.18.0
+ 3.11.4
@@ -53,5 +54,16 @@
${ststem-rules.version}test
+
+
+ com.google.protobuf
+ protobuf-java
+ ${protobuf-java.version}
+
+
+
+ com.google.protobuf
+ protobuf-java-util
+
diff --git a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/ProtoBufJsonUtils.java b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/ProtoBufJsonUtils.java
new file mode 100644
index 0000000000..09aba12a0e
--- /dev/null
+++ b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/ProtoBufJsonUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.library.util;
+
+import com.google.protobuf.Message;
+import com.google.protobuf.util.JsonFormat;
+import java.io.IOException;
+
+public class ProtoBufJsonUtils {
+
+ public static String toJSON(Message sourceMessage) throws IOException {
+ return JsonFormat.printer().print(sourceMessage);
+ }
+
+ /**
+ * Extract data from a JSON String and use them to construct a Protocol Buffers Message.
+ *
+ * @param json A JSON data string to parse
+ * @param targetBuilder A Message builder to use to construct the resulting Message
+ * @throws com.google.protobuf.InvalidProtocolBufferException Thrown in case of invalid Message data
+ */
+ public static void fromJSON(String json, Message.Builder targetBuilder) throws IOException {
+ JsonFormat.parser()
+ .usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder()
+ .add(targetBuilder.getDescriptorForType())
+ .build())
+ .ignoringUnknownFields()
+ .merge(json, targetBuilder);
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/RegisterModuleProvider.java b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/RegisterModuleProvider.java
index 798dea3d77..f7b3dc0a81 100644
--- a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/RegisterModuleProvider.java
+++ b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/RegisterModuleProvider.java
@@ -20,12 +20,16 @@ package org.apache.skywalking.oap.server.receiver.register.provider;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
+import org.apache.skywalking.oap.server.core.server.JettyHandlerRegister;
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
import org.apache.skywalking.oap.server.library.module.ModuleProvider;
import org.apache.skywalking.oap.server.receiver.register.module.RegisterModule;
import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.grpc.RegisterServiceHandler;
import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.grpc.ServiceInstancePingServiceHandler;
+import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest.ServiceInstancePingServletHandler;
+import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest.ServiceInstanceRegisterServletHandler;
+import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest.ServiceRegisterServletHandler;
import org.apache.skywalking.oap.server.receiver.sharing.server.SharingServerModule;
public class RegisterModuleProvider extends ModuleProvider {
@@ -56,6 +60,13 @@ public class RegisterModuleProvider extends ModuleProvider {
.getService(GRPCHandlerRegister.class);
grpcHandlerRegister.addHandler(new RegisterServiceHandler(getManager()));
grpcHandlerRegister.addHandler(new ServiceInstancePingServiceHandler(getManager()));
+
+ JettyHandlerRegister jettyHandlerRegister = getManager().find(SharingServerModule.NAME)
+ .provider()
+ .getService(JettyHandlerRegister.class);
+ jettyHandlerRegister.addHandler(new ServiceRegisterServletHandler(getManager()));
+ jettyHandlerRegister.addHandler(new ServiceInstanceRegisterServletHandler(getManager()));
+ jettyHandlerRegister.addHandler(new ServiceInstancePingServletHandler(getManager()));
}
@Override
diff --git a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstancePingServletHandler.java b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstancePingServletHandler.java
new file mode 100644
index 0000000000..738f6611c8
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstancePingServletHandler.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.io.IOException;
+import java.util.Objects;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.skywalking.apm.network.common.Command;
+import org.apache.skywalking.apm.network.common.Commands;
+import org.apache.skywalking.apm.network.register.v2.ServiceInstancePingPkg;
+import org.apache.skywalking.apm.network.trace.component.command.ServiceResetCommand;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.cache.ServiceInstanceInventoryCache;
+import org.apache.skywalking.oap.server.core.command.CommandService;
+import org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory;
+import org.apache.skywalking.oap.server.core.register.service.IServiceInstanceInventoryRegister;
+import org.apache.skywalking.oap.server.core.register.service.IServiceInventoryRegister;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.server.jetty.ArgumentsParseException;
+import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ServiceInstancePingServletHandler extends JettyJsonHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(ServiceInstancePingServletHandler.class);
+
+ private final IServiceInstanceInventoryRegister serviceInstanceInventoryRegister;
+ private final ServiceInstanceInventoryCache serviceInstanceInventoryCache;
+ private final IServiceInventoryRegister serviceInventoryRegister;
+ private final CommandService commandService;
+ private final Gson gson = new Gson();
+
+ public ServiceInstancePingServletHandler(ModuleManager moduleManager) {
+ this.serviceInstanceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService(
+ IServiceInstanceInventoryRegister.class);
+ this.serviceInstanceInventoryCache = moduleManager.find(CoreModule.NAME).provider().getService(
+ ServiceInstanceInventoryCache.class);
+ this.serviceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService(
+ IServiceInventoryRegister.class);
+ this.commandService = moduleManager.find(CoreModule.NAME).provider().getService(CommandService.class);
+ }
+
+ @Override
+ public String pathSpec() {
+ return "/v2/instance/heartbeat";
+ }
+
+ @Override
+ protected JsonElement doGet(HttpServletRequest req) throws ArgumentsParseException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected JsonElement doPost(HttpServletRequest req) throws ArgumentsParseException, IOException {
+ JsonObject responseJson = new JsonObject();
+
+ try {
+ ServiceInstancePingPkg.Builder builder = ServiceInstancePingPkg.newBuilder();
+ ProtoBufJsonUtils.fromJSON(getJsonBody(req), builder);
+ ServiceInstancePingPkg instancePingPkg = builder.build();
+
+ int serviceInstanceId = instancePingPkg.getServiceInstanceId();
+ long heartBeatTime = instancePingPkg.getTime();
+ String serviceInstanceUUID = instancePingPkg.getServiceInstanceUUID();
+ serviceInstanceInventoryRegister.heartbeat(serviceInstanceId, heartBeatTime);
+
+ ServiceInstanceInventory serviceInstanceInventory = serviceInstanceInventoryCache.get(serviceInstanceId);
+ if (Objects.nonNull(serviceInstanceInventory)) {
+ serviceInventoryRegister.heartbeat(serviceInstanceInventory.getServiceId(), heartBeatTime);
+ } else {
+ logger.warn(
+ "Can't found service by service instance id from cache, service instance id is: {}",
+ serviceInstanceId
+ );
+
+ final ServiceResetCommand resetCommand = commandService.newResetCommand(
+ serviceInstanceId, heartBeatTime, serviceInstanceUUID);
+ final Command command = resetCommand.serialize().build();
+ final Commands nextCommands = Commands.newBuilder().addCommands(command).build();
+ return gson.fromJson(ProtoBufJsonUtils.toJSON(nextCommands), JsonElement.class);
+ }
+
+ } catch (IOException e) {
+ responseJson.addProperty("error", e.getMessage());
+ logger.error(e.getMessage(), e);
+ }
+
+ return responseJson;
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstanceRegisterServletHandler.java b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstanceRegisterServletHandler.java
new file mode 100644
index 0000000000..f930fc37e4
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstanceRegisterServletHandler.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.skywalking.apm.network.common.KeyStringValuePair;
+import org.apache.skywalking.apm.network.register.v2.ServiceInstance;
+import org.apache.skywalking.apm.network.register.v2.ServiceInstances;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.cache.ServiceInventoryCache;
+import org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory;
+import org.apache.skywalking.oap.server.core.register.ServiceInventory;
+import org.apache.skywalking.oap.server.core.register.service.IServiceInstanceInventoryRegister;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.server.jetty.ArgumentsParseException;
+import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.HOST_NAME;
+import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.IPV4S;
+import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.LANGUAGE;
+import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.OS_NAME;
+import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.PROCESS_NO;
+
+public class ServiceInstanceRegisterServletHandler extends JettyJsonHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(ServiceInstanceRegisterServletHandler.class);
+ private static final String INSTANCE_CUSTOMIZED_NAME_PREFIX = "NAME:";
+
+ private final IServiceInstanceInventoryRegister serviceInstanceInventoryRegister;
+ private final ServiceInventoryCache serviceInventoryCache;
+
+ private static final String KEY = "key";
+ private static final String VALUE = "value";
+
+ public ServiceInstanceRegisterServletHandler(ModuleManager moduleManager) {
+ this.serviceInventoryCache = moduleManager.find(CoreModule.NAME)
+ .provider()
+ .getService(ServiceInventoryCache.class);
+ this.serviceInstanceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService(
+ IServiceInstanceInventoryRegister.class);
+ }
+
+ @Override
+ public String pathSpec() {
+ return "/v2/instance/register";
+ }
+
+ @Override
+ protected JsonElement doGet(HttpServletRequest req) throws ArgumentsParseException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected JsonElement doPost(HttpServletRequest req) throws ArgumentsParseException {
+
+ JsonObject responseJson = new JsonObject();
+ JsonArray jsonArray = new JsonArray();
+
+ try {
+ ServiceInstances.Builder builder = ServiceInstances.newBuilder();
+ ProtoBufJsonUtils.fromJSON(getJsonBody(req), builder);
+ List serviceInstances = builder.build().getInstancesList();
+
+ serviceInstances.forEach(instance -> {
+ long time = instance.getTime();
+ int serviceId = instance.getServiceId();
+ String instanceUUID = instance.getInstanceUUID();
+
+ JsonObject instanceProperties = new JsonObject();
+ List ipv4s = new ArrayList<>();
+
+ for (KeyStringValuePair property : instance.getPropertiesList()) {
+ String key = property.getKey();
+ switch (key) {
+ case HOST_NAME:
+ instanceProperties.addProperty(HOST_NAME, property.getValue());
+ break;
+ case OS_NAME:
+ instanceProperties.addProperty(OS_NAME, property.getValue());
+ break;
+ case LANGUAGE:
+ instanceProperties.addProperty(LANGUAGE, property.getValue());
+ break;
+ case "ipv4":
+ ipv4s.add(property.getValue());
+ break;
+ case PROCESS_NO:
+ instanceProperties.addProperty(PROCESS_NO, property.getValue());
+ break;
+ default:
+ instanceProperties.addProperty(key, property.getValue());
+ }
+ }
+ instanceProperties.addProperty(IPV4S, ServiceInstanceInventory.PropertyUtil.ipv4sSerialize(ipv4s));
+
+ String instanceName = null;
+ if (instanceUUID.startsWith(INSTANCE_CUSTOMIZED_NAME_PREFIX)) {
+ instanceName = instanceUUID.substring(INSTANCE_CUSTOMIZED_NAME_PREFIX.length());
+ }
+
+ ServiceInventory serviceInventory = serviceInventoryCache.get(serviceId);
+
+ if (instanceName == null) {
+ instanceName = serviceInventory.getName();
+ if (instanceProperties.has(PROCESS_NO)) {
+ instanceName += "-pid:" + instanceProperties.get(PROCESS_NO).getAsString();
+ }
+ if (instanceProperties.has(HOST_NAME)) {
+ instanceName += "@" + instanceProperties.get(HOST_NAME).getAsString();
+ }
+ }
+ int instanceId = serviceInstanceInventoryRegister.getOrCreate(
+ serviceId, instanceName, instanceUUID, time, instanceProperties);
+
+ responseJson.addProperty(KEY, instanceUUID);
+ responseJson.addProperty(VALUE, instanceId);
+ jsonArray.add(responseJson);
+ });
+
+ return jsonArray;
+ } catch (IOException e) {
+ responseJson.addProperty("error", e.getMessage());
+ logger.error(e.getMessage(), e);
+ }
+ return responseJson;
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceRegisterServletHandler.java b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceRegisterServletHandler.java
new file mode 100644
index 0000000000..10242bc0d3
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceRegisterServletHandler.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.skywalking.apm.network.register.v2.Service;
+import org.apache.skywalking.apm.network.register.v2.Services;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.register.NodeType;
+import org.apache.skywalking.oap.server.core.register.service.IServiceInventoryRegister;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.server.jetty.ArgumentsParseException;
+import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ServiceRegisterServletHandler extends JettyJsonHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(ServiceRegisterServletHandler.class);
+
+ private final IServiceInventoryRegister serviceInventoryRegister;
+
+ private static final String KEY = "key";
+ private static final String VALUE = "value";
+
+ public ServiceRegisterServletHandler(ModuleManager moduleManager) {
+ serviceInventoryRegister = moduleManager.find(CoreModule.NAME)
+ .provider()
+ .getService(IServiceInventoryRegister.class);
+ }
+
+ @Override
+ public String pathSpec() {
+ return "/v2/service/register";
+ }
+
+ @Override
+ protected JsonElement doGet(HttpServletRequest req) throws ArgumentsParseException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected JsonElement doPost(HttpServletRequest req) throws ArgumentsParseException {
+ JsonArray responseArray = new JsonArray();
+
+ try {
+ Services.Builder builder = Services.newBuilder();
+ ProtoBufJsonUtils.fromJSON(getJsonBody(req), builder);
+ List serviceList = builder.build().getServicesList();
+
+ serviceList.forEach(service -> {
+ int serviceId = serviceInventoryRegister.getOrCreate(service.getServiceName(),
+ NodeType.fromRegisterServiceType(
+ service.getType()), null
+ );
+
+ JsonObject mapping = new JsonObject();
+ mapping.addProperty(KEY, service.getServiceName());
+ mapping.addProperty(VALUE, serviceId);
+ responseArray.add(mapping);
+ });
+ } catch (IOException e) {
+ JsonObject mapping = new JsonObject();
+ mapping.addProperty("error", e.getMessage());
+ responseArray.add(mapping);
+ logger.error(e.getMessage(), e);
+ }
+ return responseArray;
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/TraceModuleProvider.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/TraceModuleProvider.java
index 3eb3d5f1ff..b087241564 100755
--- a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/TraceModuleProvider.java
+++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/TraceModuleProvider.java
@@ -18,10 +18,12 @@
package org.apache.skywalking.oap.server.receiver.trace.provider;
+import java.io.IOException;
import org.apache.skywalking.oap.server.configuration.api.ConfigurationModule;
import org.apache.skywalking.oap.server.configuration.api.DynamicConfigurationService;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
+import org.apache.skywalking.oap.server.core.server.JettyHandlerRegister;
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
import org.apache.skywalking.oap.server.library.module.ModuleProvider;
@@ -30,6 +32,7 @@ import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedExcepti
import org.apache.skywalking.oap.server.receiver.sharing.server.SharingServerModule;
import org.apache.skywalking.oap.server.receiver.trace.module.TraceModule;
import org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.grpc.TraceSegmentReportServiceHandler;
+import org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.TraceSegmentCollectServletHandler;
import org.apache.skywalking.oap.server.receiver.trace.provider.parser.ISegmentParserService;
import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentParseV2;
import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentParserListenerManager;
@@ -41,8 +44,6 @@ import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.
import org.apache.skywalking.oap.server.receiver.trace.provider.parser.standardization.SegmentStandardizationWorker;
import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
-import java.io.IOException;
-
public class TraceModuleProvider extends ModuleProvider {
private final TraceServiceModuleConfig moduleConfig;
@@ -80,7 +81,8 @@ public class TraceModuleProvider extends ModuleProvider {
segmentProducerV2 = new SegmentParseV2.Producer(getManager(), listenerManager(), moduleConfig);
- this.registerServiceImplementation(ISegmentParserService.class, new SegmentParserServiceImpl(segmentProducerV2));
+ this.registerServiceImplementation(
+ ISegmentParserService.class, new SegmentParserServiceImpl(segmentProducerV2));
}
public SegmentParserListenerManager listenerManager() {
@@ -99,19 +101,28 @@ public class TraceModuleProvider extends ModuleProvider {
public void start() throws ModuleStartException {
DynamicConfigurationService dynamicConfigurationService = getManager().find(ConfigurationModule.NAME)
.provider()
- .getService(DynamicConfigurationService.class);
+ .getService(
+ DynamicConfigurationService.class);
GRPCHandlerRegister grpcHandlerRegister = getManager().find(SharingServerModule.NAME)
.provider()
.getService(GRPCHandlerRegister.class);
+ JettyHandlerRegister jettyHandlerRegister = getManager().find(SharingServerModule.NAME)
+ .provider()
+ .getService(JettyHandlerRegister.class);
try {
dynamicConfigurationService.registerConfigChangeWatcher(thresholds);
dynamicConfigurationService.registerConfigChangeWatcher(uninstrumentedGatewaysConfig);
grpcHandlerRegister.addHandler(new TraceSegmentReportServiceHandler(segmentProducerV2, getManager()));
- SegmentStandardizationWorker standardizationWorkerV2 = new SegmentStandardizationWorker(getManager(), segmentProducerV2, moduleConfig
- .getBufferPath(), moduleConfig.getBufferOffsetMaxFileSize(), moduleConfig.getBufferDataMaxFileSize(), moduleConfig
- .isBufferFileCleanWhenRestart());
+ jettyHandlerRegister.addHandler(new TraceSegmentCollectServletHandler(segmentProducerV2));
+
+ SegmentStandardizationWorker standardizationWorkerV2 = new SegmentStandardizationWorker(
+ getManager(), segmentProducerV2, moduleConfig
+ .getBufferPath(), moduleConfig.getBufferOffsetMaxFileSize(), moduleConfig.getBufferDataMaxFileSize(),
+ moduleConfig
+ .isBufferFileCleanWhenRestart()
+ );
segmentProducerV2.setStandardizationWorker(standardizationWorkerV2);
} catch (IOException e) {
throw new ModuleStartException(e.getMessage(), e);
diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/TraceSegmentCollectServletHandler.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/TraceSegmentCollectServletHandler.java
new file mode 100644
index 0000000000..44bb24320c
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/TraceSegmentCollectServletHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest;
+
+import com.google.gson.JsonElement;
+import java.io.BufferedReader;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.skywalking.apm.network.language.agent.UpstreamSegment;
+import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler;
+import org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.reader.UpstreamSegmentJsonReader;
+import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentParseV2;
+import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TraceSegmentCollectServletHandler extends JettyJsonHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(TraceSegmentCollectServletHandler.class);
+
+ private final SegmentParseV2.Producer segmentProducer;
+
+ private UpstreamSegmentJsonReader upstreamSegmentJsonReader = new UpstreamSegmentJsonReader();
+
+ public TraceSegmentCollectServletHandler(SegmentParseV2.Producer segmentProducer) {
+ this.segmentProducer = segmentProducer;
+ }
+
+ @Override
+ public String pathSpec() {
+ return "/v2/segments";
+ }
+
+ @Override
+ protected JsonElement doGet(HttpServletRequest req) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected JsonElement doPost(HttpServletRequest req) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("receive stream segment");
+ }
+
+ StringBuilder stringBuilder = new StringBuilder();
+ String line = null;
+ try {
+ BufferedReader reader = req.getReader();
+ while ((line = reader.readLine()) != null) {
+ stringBuilder.append(line);
+ }
+ UpstreamSegment upstreamSegment = upstreamSegmentJsonReader.read(stringBuilder.toString()).build();
+
+ segmentProducer.send(upstreamSegment, SegmentSource.Agent);
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+
+ return null;
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/SegmentJsonReader.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/SegmentJsonReader.java
new file mode 100644
index 0000000000..2e78dc1cf4
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/SegmentJsonReader.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.reader;
+
+import java.io.IOException;
+import org.apache.skywalking.apm.network.language.agent.v2.SegmentObject;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+
+public class SegmentJsonReader implements StreamJsonReader {
+
+ @Override
+ public SegmentObject.Builder read(String json) throws IOException {
+ SegmentObject.Builder builder = SegmentObject.newBuilder();
+ ProtoBufJsonUtils.fromJSON(json, builder);
+ return builder;
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/StreamJsonReader.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/StreamJsonReader.java
new file mode 100644
index 0000000000..13b7028c90
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/StreamJsonReader.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.reader;
+
+import java.io.IOException;
+
+public interface StreamJsonReader {
+ T read(String json) throws IOException;
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/UpstreamSegmentJsonReader.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/UpstreamSegmentJsonReader.java
new file mode 100644
index 0000000000..3344952b53
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/UpstreamSegmentJsonReader.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.reader;
+
+import java.io.IOException;
+import org.apache.skywalking.apm.network.language.agent.UpstreamSegment;
+import org.apache.skywalking.apm.network.language.agent.v2.SegmentObject;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+
+public class UpstreamSegmentJsonReader implements StreamJsonReader {
+
+ private final SegmentJsonReader segmentJsonReader = new SegmentJsonReader();
+
+ @Override
+ public UpstreamSegment.Builder read(String json) throws IOException {
+ UpstreamSegment.Builder upstreamSegmentBuilder = UpstreamSegment.newBuilder();
+ ProtoBufJsonUtils.fromJSON(json, upstreamSegmentBuilder);
+
+ SegmentObject.Builder segmentBuilder = segmentJsonReader.read(json);
+
+ upstreamSegmentBuilder.setSegment(segmentBuilder.build().toByteString());
+
+ return upstreamSegmentBuilder;
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/segment/ProtoBufJsonUtilsTest.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/segment/ProtoBufJsonUtilsTest.java
new file mode 100644
index 0000000000..559a90876e
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/segment/ProtoBufJsonUtilsTest.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.segment;
+
+import java.io.IOException;
+import org.apache.skywalking.apm.network.common.Command;
+import org.apache.skywalking.apm.network.common.Commands;
+import org.apache.skywalking.apm.network.language.agent.UpstreamSegment;
+import org.apache.skywalking.apm.network.language.agent.v2.SegmentObject;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ProtoBufJsonUtilsTest {
+ @Test
+ public void testProtoBuf() {
+ String json = "{\n" +
+ " \"spans\": [\n" +
+ " {\n" +
+ " \"operationName\": \"/tier2/lb\",\n" +
+ " \"startTime\": 1582526028207,\n" +
+ " \"endTime\": 1582526028221,\n" +
+ " \"spanType\": \"Exit\",\n" +
+ " \"spanId\": 1,\n" +
+ " \"isError\": false,\n" +
+ " \"parentSpanId\": 0,\n" +
+ " \"componentId\": 6000,\n" +
+ " \"peer\": \"User Service Name-nginx:upstream_ip:port\",\n" +
+ " \"spanLayer\": \"HTTP\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"operationName\": \"/tier2/lb\",\n" +
+ " \"startTime\": 1582526028207,\n" +
+ " \"tags\": [\n" +
+ " {\n" +
+ " \"key\": \"http.method\",\n" +
+ " \"value\": \"GET\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"key\": \"http.params\",\n" +
+ " \"value\": \"http://127.0.0.1/tier2/lb\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"endTime\": 1582526028221,\n" +
+ " \"spanType\": \"Entry\",\n" +
+ " \"spanId\": 0,\n" +
+ " \"isError\": false,\n" +
+ " \"parentSpanId\": -1,\n" +
+ " \"componentId\": 6000,\n" +
+ " \"refs\": [\n" +
+ " {\n" +
+ " \"parentTraceSegmentId\": {\n" +
+ " \"idParts\": [\n" +
+ " 1582526028032,\n" +
+ " 794206293,\n" +
+ " 69887\n" +
+ " ]\n" +
+ " },\n" +
+ " \"parentEndpointId\": 0,\n" +
+ " \"entryEndpointId\": 0,\n" +
+ " \"parentServiceInstanceId\": 1,\n" +
+ " \"parentEndpoint\": \"/ingress\",\n" +
+ " \"networkAddress\": \"#User Service Name-nginx:upstream_ip:port\",\n" +
+ " \"parentSpanId\": 1,\n" +
+ " \"entryServiceInstanceId\": 1,\n" +
+ " \"networkAddressId\": 0,\n" +
+ " \"entryEndpoint\": \"/ingress\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"spanLayer\": \"HTTP\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"serviceInstanceId\": 1,\n" +
+ " \"serviceId\": 1,\n" +
+ " \"traceSegmentId\": {\n" +
+ " \"idParts\": [\n" +
+ " 1582526028040,\n" +
+ " 794206293,\n" +
+ " 69887\n" +
+ " ]\n" +
+ " },\n" +
+ " \"globalTraceIds\": [\n" +
+ " {\n" +
+ " \"idParts\": [\n" +
+ " 1582526028032,\n" +
+ " 794206293,\n" +
+ " 69887\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+
+ UpstreamSegment.Builder builder = UpstreamSegment.newBuilder();
+ try {
+ ProtoBufJsonUtils.fromJSON(json, builder);
+ UpstreamSegment upstreamSegment = builder.build();
+ Assert.assertEquals(1582526028032L, upstreamSegment.getGlobalTraceIds(0).getIdParts(0));
+
+ SegmentObject.Builder segBuilder = SegmentObject.newBuilder();
+ ProtoBufJsonUtils.fromJSON(json, segBuilder);
+ SegmentObject segmentObject = segBuilder.build();
+ Assert.assertEquals(2, segmentObject.getSpansCount());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Test
+ public void testToJson() {
+ String json = "{\n" +
+ " \"commands\": [{\n" +
+ " }]\n" +
+ "}";
+ try {
+ Command command = Command.newBuilder().build();
+ final Commands nextCommands = Commands.newBuilder().addCommands(command).build();
+ Assert.assertEquals(json, ProtoBufJsonUtils.toJSON(nextCommands));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/pom.xml b/test/e2e/e2e-http-api-with-nginx-lua/pom.xml
new file mode 100755
index 0000000000..012723a7fe
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/pom.xml
@@ -0,0 +1,195 @@
+
+
+
+
+
+ apache-skywalking-e2e
+ org.apache.skywalking
+ 1.0.0
+
+ 4.0.0
+
+ e2e-http-api-with-nginx-lua
+
+
+ 1.1
+ skywalking-e2e-container-${build.id}-http-api-with-nginx-lua
+
+
+
+
+
+ org.apache.skywalking
+ e2e-base
+ ${project.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+ true
+ true
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+ %a-%t-%i
+
+
+ skyapm/e2e-container:${e2e.container.version}
+ e2e-container-app-for-nginx-lua
+
+
+ ${project.build.finalName}.jar
+ -Dskywalking.agent.cool_down_threshold=1
+
+
+
+ +webapp.host:webapp.port:8080
+ +client.host:client.port:9090
+
+
+
+ ${sw.home}:/sw
+ ${project.build.directory}:/home
+
+ ${project.basedir}/src/docker/rc.d/rc1-startup.sh:/rc.d/rc1-startup.sh:ro
+
+
+
+
+
+
+ http://${docker.host.address}:${client.port}/e2e/info
+
+ GET
+ 200
+
+
+
+
+
+
+
+ openresty/openresty
+ ${e2e.container.name.prefix}-openresty
+
+
+
+ c6aca516200573a64db7060cefb5d3608c571c3a
+
+
+
+ e2e-container-app-for-nginx-lua:upstream
+
+
+ +nginx.host:nginx.port:8080
+
+
+
+ ${project.basedir}/src/docker/rc.d:/rc.d:ro
+
+
+
+
+
+ http://${docker.host.address}:${nginx.port}
+
+
+
+
+ /rc.d/rc0-prepare.sh
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+
+ ${webapp.host}
+
+
+ ${webapp.port}
+
+
+ ${client.host}
+
+
+ ${client.port}
+
+
+ ${nginx.port}
+
+
+
+
+
+
+ verify
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+
+ http://${nginx.host}:${nginx.port}/ingress
+
+
+
+
+
+
+ verify
+
+
+
+
+
+
+
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc0-prepare.sh b/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc0-prepare.sh
new file mode 100755
index 0000000000..39b7ed5e2f
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc0-prepare.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+# Licensed to the SkyAPM under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#!/bin/bash
+
+set -e
+
+var_application_file="/var/nginx/conf.d/nginx.conf"
+
+var_upstream_ip=$(cat /etc/hosts|grep upstream|awk '{print $1'})
+
+apt-get update && apt-get -y install git
+
+echo 'git clone skyalking-nginx-lua lib from https://github.com/apache/skywalking-nginx-lua.git'
+
+git clone https://github.com/apache/skywalking-nginx-lua.git /usr/share/skywalking-nginx-lua \
+ && cd /usr/share/skywalking-nginx-lua \
+ && git checkout ${SKYWALKING_NINGX_LUA_GIT_COMMIT_ID} \
+ && ls ./ \
+ && mkdir -p /var/nginx/conf.d
+
+
+generateNginxConf() {
+ cat <> ${var_application_file}
+worker_processes 1;
+daemon off;
+error_log /dev/stdout error;
+
+events {
+ worker_connections 1024;
+}
+http {
+ lua_package_path "/usr/share/skywalking-nginx-lua/lib/skywalking/?.lua;;";
+ # Buffer represents the register inform and the queue of the finished segment
+ lua_shared_dict tracing_buffer 100m;
+
+ # Init is the timer setter and keeper
+ # Setup an infinite loop timer to do register and trace report.
+ init_worker_by_lua_block {
+ local metadata_buffer = ngx.shared.tracing_buffer
+
+ metadata_buffer:set('serviceName', 'User_Service_Name')
+ -- Instance means the number of Nginx deloyment, does not mean the worker instances
+ metadata_buffer:set('serviceInstanceName', 'User_Service_Instance_Name')
+
+ require("client"):startBackendTimer("http://${var_upstream_ip}:12800")
+ }
+ log_format sw_trace escape=json "$uri $request_body";
+
+ server {
+ listen 8080;
+
+ location /nginx/e2e/info {
+
+ rewrite_by_lua_block {
+ require("tracer"):start("User_Service_Name")
+ }
+
+ proxy_pass http://upstream:9090/e2e/info;
+
+ body_filter_by_lua_block {
+ require("tracer"):finish()
+ }
+
+ log_by_lua_block {
+ require("tracer"):prepareForReport()
+ }
+ }
+ }
+}
+EOT
+}
+
+generateNginxConf;
+
+echo 'generated nginx.conf:'
+
+cat ${var_application_file}
+
+/usr/bin/openresty -c ${var_application_file}
+sync
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc1-startup.sh b/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc1-startup.sh
new file mode 100755
index 0000000000..9328f7c6e7
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc1-startup.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+# Licensed to the SkyAPM under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo 'starting OAP server...' \
+ && SW_STORAGE_ES_BULK_ACTIONS=1 \
+ && SW_STORAGE_ES_FLUSH_INTERVAL=1 \
+ && SW_RECEIVER_BUFFER_PATH=/tmp/oap/trace_buffer1 \
+ && SW_SERVICE_MESH_BUFFER_PATH=/tmp/oap/mesh_buffer1 \
+ && start_oap 'init'
+
+echo 'starting Web app...' \
+ && start_webapp '0.0.0.0' 8080
+
+echo 'starting instrumented services...' \
+ && start_instrumented_services
+
+check_tcp 127.0.0.1 \
+ 9090 \
+ 60 \
+ 10 \
+ "waiting for the instrumented service to be ready"
+
+if [[ $? -ne 0 ]]; then
+ echo "instrumented service failed to start in 30 * 10 seconds: "
+ cat ${SERVICE_LOG}/*
+ exit 1
+fi
+
+echo "SkyWalking e2e container is ready for tests"
+
+tail -f ${OAP_LOG_DIR}/* \
+ ${WEBAPP_LOG_DIR}/* \
+ ${SERVICE_LOG}/* \
+ ${ES_HOME}/logs/stdout.log
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java b/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java
new file mode 100644
index 0000000000..c661c173e2
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.e2e.sample.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SampleClientApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(SampleClientApplication.class, args);
+ }
+}
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java b/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java
new file mode 100644
index 0000000000..03319c73b0
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.e2e.sample.client;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/e2e")
+public class TestController {
+ @GetMapping("/info")
+ public String hello() {
+ return "e2e-container-info";
+ }
+}
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/main/resources/application.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/main/resources/application.yml
new file mode 100644
index 0000000000..83d1e3df9a
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/main/resources/application.yml
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+server:
+ port: 9090
+
+spring:
+ main:
+ banner-mode: 'off'
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java b/test/e2e/e2e-http-api-with-nginx-lua/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java
new file mode 100644
index 0000000000..33eafb4887
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.e2e;
+
+import java.io.InputStream;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.skywalking.e2e.metrics.AtLeastOneOfMetricsMatcher;
+import org.apache.skywalking.e2e.metrics.Metrics;
+import org.apache.skywalking.e2e.metrics.MetricsQuery;
+import org.apache.skywalking.e2e.metrics.MetricsValueMatcher;
+import org.apache.skywalking.e2e.service.Service;
+import org.apache.skywalking.e2e.service.ServicesMatcher;
+import org.apache.skywalking.e2e.service.ServicesQuery;
+import org.apache.skywalking.e2e.service.endpoint.Endpoint;
+import org.apache.skywalking.e2e.service.endpoint.EndpointQuery;
+import org.apache.skywalking.e2e.service.endpoint.Endpoints;
+import org.apache.skywalking.e2e.service.endpoint.EndpointsMatcher;
+import org.apache.skywalking.e2e.service.instance.Instance;
+import org.apache.skywalking.e2e.service.instance.Instances;
+import org.apache.skywalking.e2e.service.instance.InstancesMatcher;
+import org.apache.skywalking.e2e.service.instance.InstancesQuery;
+import org.apache.skywalking.e2e.topo.Call;
+import org.apache.skywalking.e2e.topo.ServiceInstanceTopoData;
+import org.apache.skywalking.e2e.topo.ServiceInstanceTopoMatcher;
+import org.apache.skywalking.e2e.topo.ServiceInstanceTopoQuery;
+import org.apache.skywalking.e2e.topo.TopoData;
+import org.apache.skywalking.e2e.topo.TopoMatcher;
+import org.apache.skywalking.e2e.topo.TopoQuery;
+import org.apache.skywalking.e2e.trace.Trace;
+import org.apache.skywalking.e2e.trace.TracesMatcher;
+import org.apache.skywalking.e2e.trace.TracesQuery;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.web.client.RestTemplate;
+import org.yaml.snakeyaml.Yaml;
+
+import static org.apache.skywalking.e2e.metrics.MetricsMatcher.verifyMetrics;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_ENDPOINT_METRICS;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_INSTANCE_METRICS;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_INSTANCE_RELATION_CLIENT_METRICS;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_INSTANCE_RELATION_SERVER_METRICS;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_METRICS;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_RELATION_CLIENT_METRICS;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_RELATION_SERVER_METRICS;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+public class SampleVerificationITCase {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SampleVerificationITCase.class);
+
+ private final RestTemplate restTemplate = new RestTemplate();
+ private final int retryInterval = 30;
+
+ private SimpleQueryClient queryClient;
+ private String nginxServiceUrl;
+
+ @Before
+ public void setUp() {
+ final String swWebappHost = System.getProperty("sw.webapp.host", "127.0.0.1");
+ final String swWebappPort = System.getProperty("sw.webapp.port", "32783");
+ final String nginxHost = System.getProperty("nginx.host", "127.0.0.1");
+ final String nginxPort = System.getProperty("nginx.port", "32782");
+ queryClient = new SimpleQueryClient(swWebappHost, swWebappPort);
+ nginxServiceUrl = "http://" + nginxHost + ":" + nginxPort;
+ }
+
+ @Test(timeout = 1200000)
+ @DirtiesContext
+ public void verify() throws Exception {
+ final LocalDateTime minutesAgo = LocalDateTime.now(ZoneOffset.UTC);
+
+ while (true) {
+ final List services = queryClient.services(
+ new ServicesQuery().start(minutesAgo).end(LocalDateTime.now()));
+
+ if (!services.isEmpty() && services.size() >= 2) {
+ break;
+ }
+ Thread.sleep(10000L);
+ }
+
+ while (true) {
+ try {
+ final Map user = new HashMap<>();
+ user.put("name", "SkyWalking");
+ final ResponseEntity responseEntity = restTemplate.getForEntity(
+ nginxServiceUrl + "/nginx/e2e/info", String.class);
+ LOGGER.info("responseEntity: {}", responseEntity);
+ assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(responseEntity.getBody()).isEqualTo("e2e-container-info");
+ final List traces = queryClient.traces(new TracesQuery().start(minutesAgo)
+ .end(LocalDateTime.now())
+ .orderByDuration());
+ if (!traces.isEmpty() && traces.size() >= 2) {
+ break;
+ }
+ Thread.sleep(10000L);
+ } catch (Exception ignored) {
+ }
+ }
+
+ doRetryableVerification(() -> {
+ try {
+ verifyTraces(minutesAgo);
+ } catch (Exception e) {
+ LOGGER.warn(e.getMessage(), e);
+ }
+ });
+
+ doRetryableVerification(() -> {
+ try {
+ verifyServices(minutesAgo);
+ } catch (Exception e) {
+ LOGGER.warn(e.getMessage(), e);
+ }
+ });
+
+ doRetryableVerification(() -> {
+ try {
+ verifyTopo(minutesAgo);
+ } catch (Exception e) {
+ LOGGER.warn(e.getMessage(), e);
+ }
+ });
+
+ doRetryableVerification(() -> {
+ try {
+ verifyServiceInstanceTopo(minutesAgo);
+ } catch (Exception e) {
+ LOGGER.warn(e.getMessage(), e);
+ }
+ });
+ }
+
+ private void verifyTopo(LocalDateTime minutesAgo) throws Exception {
+ final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
+
+ final TopoData topoData = queryClient.topo(new TopoQuery().stepByMinute()
+ .start(minutesAgo.minusDays(1))
+ .end(now));
+ LOGGER.info("topoData: {}", topoData);
+
+ InputStream expectedInputStream = new ClassPathResource(
+ "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml")
+ .getInputStream();
+
+ final TopoMatcher topoMatcher = new Yaml().loadAs(expectedInputStream, TopoMatcher.class);
+ topoMatcher.verify(topoData);
+ verifyServiceRelationMetrics(topoData.getCalls(), minutesAgo);
+ }
+
+ private void verifyServiceInstanceTopo(LocalDateTime minutesAgo) throws Exception {
+ final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
+
+ final ServiceInstanceTopoData topoData = queryClient.serviceInstanceTopo(
+ new ServiceInstanceTopoQuery().stepByMinute()
+ .start(minutesAgo
+ .minusDays(1))
+ .end(now)
+ .clientServiceId("1")
+ .serverServiceId("3"));
+ LOGGER.info("instanceTopoData: {}", topoData);
+
+ InputStream expectedInputStream = new ClassPathResource(
+ "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml")
+ .getInputStream();
+
+ final ServiceInstanceTopoMatcher topoMatcher = new Yaml().loadAs(
+ expectedInputStream, ServiceInstanceTopoMatcher.class);
+ topoMatcher.verify(topoData);
+ verifyServiceInstanceRelationMetrics(topoData.getCalls(), minutesAgo);
+ }
+
+ private void verifyServices(LocalDateTime minutesAgo) throws Exception {
+ final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
+
+ final List services = queryClient.services(new ServicesQuery().start(minutesAgo).end(now));
+ LOGGER.info("services: {}", services);
+
+ InputStream expectedInputStream = new ClassPathResource(
+ "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml")
+ .getInputStream();
+
+ final ServicesMatcher servicesMatcher = new Yaml().loadAs(expectedInputStream, ServicesMatcher.class);
+ servicesMatcher.verify(services);
+
+ for (Service service : services) {
+ LOGGER.info("verifying service instances: {}", service);
+
+ verifyServiceMetrics(service);
+
+ Instances instances = verifyServiceInstances(minutesAgo, now, service);
+
+ verifyInstancesMetrics(instances);
+
+ Endpoints endpoints = verifyServiceEndpoints(minutesAgo, now, service);
+
+ verifyEndpointsMetrics(endpoints);
+ }
+ }
+
+ private Instances verifyServiceInstances(LocalDateTime minutesAgo, LocalDateTime now,
+ Service service) throws Exception {
+ Instances instances = queryClient.instances(new InstancesQuery().serviceId(service.getKey())
+ .start(minutesAgo)
+ .end(now));
+ LOGGER.info("instances: {}", instances);
+ InputStream expectedInputStream;
+ if ("User_Service_Name".equals(service.getLabel())) {
+ expectedInputStream = new ClassPathResource(
+ "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.nginxInstances.yml")
+ .getInputStream();
+ } else {
+ expectedInputStream = new ClassPathResource(
+ "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml")
+ .getInputStream();
+ }
+ final InstancesMatcher instancesMatcher = new Yaml().loadAs(expectedInputStream, InstancesMatcher.class);
+ instancesMatcher.verify(instances);
+ return instances;
+ }
+
+ private Endpoints verifyServiceEndpoints(LocalDateTime minutesAgo, LocalDateTime now,
+ Service service) throws Exception {
+ Endpoints endpoints = queryClient.endpoints(new EndpointQuery().serviceId(service.getKey()));
+ LOGGER.info("instances: {}", endpoints);
+ InputStream expectedInputStream;
+
+ if ("/e2e/health-check".equals(endpoints.getEndpoints().get(0).getLabel())) {
+ expectedInputStream = new ClassPathResource(
+ "org.apache.skywalking.e2e.SampleVerificationITCase.nginxEndpoints.yml")
+ .getInputStream();
+ } else {
+ expectedInputStream = new ClassPathResource(
+ "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml")
+ .getInputStream();
+ }
+ final EndpointsMatcher endpointsMatcher = new Yaml().loadAs(expectedInputStream, EndpointsMatcher.class);
+ endpointsMatcher.verify(endpoints);
+ return endpoints;
+ }
+
+ private void verifyInstancesMetrics(Instances instances) throws Exception {
+ for (Instance instance : instances.getInstances()) {
+ for (String metricsName : ALL_INSTANCE_METRICS) {
+ LOGGER.info("verifying service instance response time: {}", instance);
+ final Metrics instanceMetrics = queryClient.metrics(new MetricsQuery().stepByMinute()
+ .metricsName(metricsName)
+ .id(instance.getKey()));
+ LOGGER.info("instanceMetrics: {}", instanceMetrics);
+ AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
+ MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
+ greaterThanZero.setValue("ge 0");
+ instanceRespTimeMatcher.setValue(greaterThanZero);
+ instanceRespTimeMatcher.verify(instanceMetrics);
+ LOGGER.info("{}: {}", metricsName, instanceMetrics);
+ }
+ }
+ }
+
+ private void verifyEndpointsMetrics(Endpoints endpoints) throws Exception {
+ for (Endpoint endpoint : endpoints.getEndpoints()) {
+ if (!endpoint.getLabel().equals("/e2e/info") || !endpoint.getLabel()
+ .equals("/nginx/e2e/info")) {
+ continue;
+ }
+ for (String metricName : ALL_ENDPOINT_METRICS) {
+ LOGGER.info("verifying endpoint {}, metrics: {}", endpoint, metricName);
+ final Metrics metrics = queryClient.metrics(new MetricsQuery().stepByMinute()
+ .metricsName(metricName)
+ .id(endpoint.getKey()));
+ LOGGER.info("metrics: {}", metrics);
+ AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
+ MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
+ greaterThanZero.setValue("ge 0");
+ instanceRespTimeMatcher.setValue(greaterThanZero);
+ instanceRespTimeMatcher.verify(metrics);
+ LOGGER.info("{}: {}", metricName, metrics);
+ }
+ }
+ }
+
+ private void verifyServiceMetrics(Service service) throws Exception {
+ for (String metricName : ALL_SERVICE_METRICS) {
+ LOGGER.info("verifying service {}, metrics: {}", service, metricName);
+ final Metrics serviceMetrics = queryClient.metrics(new MetricsQuery().stepByMinute()
+ .metricsName(metricName)
+ .id(service.getKey()));
+ LOGGER.info("serviceMetrics: {}", serviceMetrics);
+ AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
+ MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
+ greaterThanZero.setValue("ge 0");
+ instanceRespTimeMatcher.setValue(greaterThanZero);
+ instanceRespTimeMatcher.verify(serviceMetrics);
+ LOGGER.info("{}: {}", metricName, serviceMetrics);
+ }
+ }
+
+ private void verifyTraces(LocalDateTime minutesAgo) throws Exception {
+ final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
+
+ final List traces = queryClient.traces(new TracesQuery().start(minutesAgo).end(now).orderByDuration());
+ LOGGER.info("traces: {}", traces);
+
+ InputStream expectedInputStream = new ClassPathResource(
+ "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml")
+ .getInputStream();
+
+ final TracesMatcher tracesMatcher = new Yaml().loadAs(expectedInputStream, TracesMatcher.class);
+ tracesMatcher.verifyLoosely(traces);
+ }
+
+ private void verifyServiceInstanceRelationMetrics(List calls,
+ final LocalDateTime minutesAgo) throws Exception {
+ verifyRelationMetrics(
+ calls, minutesAgo, ALL_SERVICE_INSTANCE_RELATION_CLIENT_METRICS,
+ ALL_SERVICE_INSTANCE_RELATION_SERVER_METRICS
+ );
+ }
+
+ private void verifyServiceRelationMetrics(List calls, final LocalDateTime minutesAgo) throws Exception {
+ verifyRelationMetrics(
+ calls, minutesAgo, ALL_SERVICE_RELATION_CLIENT_METRICS, ALL_SERVICE_RELATION_SERVER_METRICS);
+ }
+
+ private void verifyRelationMetrics(List calls, final LocalDateTime minutesAgo, String[] relationClientMetrics,
+ String[] relationServerMetrics) throws Exception {
+ for (Call call : calls) {
+ for (String detectPoint : call.getDetectPoints()) {
+ switch (detectPoint) {
+ case "CLIENT": {
+ for (String metricName : relationClientMetrics) {
+ verifyMetrics(queryClient, metricName, call.getId(), minutesAgo);
+ }
+ break;
+ }
+ case "SERVER": {
+ for (String metricName : relationServerMetrics) {
+ verifyMetrics(queryClient, metricName, call.getId(), minutesAgo);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private void doRetryableVerification(Runnable runnable) throws InterruptedException {
+ while (true) {
+ try {
+ runnable.run();
+ break;
+ } catch (Throwable ignored) {
+ Thread.sleep(retryInterval);
+ }
+ }
+ }
+}
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml
new file mode 100644
index 0000000000..ed06316a40
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml
@@ -0,0 +1,28 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+endpoints:
+ - key: not null
+ label: /e2e/info
+
+
+
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml
new file mode 100644
index 0000000000..26bb314d2b
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+instances:
+ - key: 2
+ label: not null
+ attributes:
+ - name: os_name
+ value: not null
+ - name: host_name
+ value: not null
+ - name: process_no
+ value: gt 0
+ - name: ipv4s
+ value: not null
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml
new file mode 100644
index 0000000000..6661a99b46
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml
@@ -0,0 +1,41 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+nodes:
+ - id: 1
+ name: User
+ type: USER
+ serviceId: 1
+ serviceName: User
+ isReal: false
+ - id: 3
+ name: not null
+ serviceId: 3
+ serviceName: User_Service_Name
+ type: not null
+ isReal: true
+calls:
+ - id: 1_3
+ source: 1
+ detectPoints:
+ - SERVER
+ target: 3
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml
new file mode 100644
index 0000000000..ef0df12df8
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+services:
+ - key: 2
+ label: "Your_ApplicationName"
+ - key: 3
+ label: "User_Service_Name"
\ No newline at end of file
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml
new file mode 100644
index 0000000000..2ca7456216
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml
@@ -0,0 +1,47 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+nodes:
+ - id: 1
+ name: User
+ type: USER
+ isReal: false
+ - id: 2
+ name: Your_ApplicationName
+ type: Tomcat
+ isReal: true
+ - id: 3
+ name: User_Service_Name
+ type: Nginx
+ isReal: true
+calls:
+ - id: 1_3
+ source: 1
+ detectPoints:
+ - SERVER
+ target: 3
+ - id: 3_2
+ source: 3
+ detectPoints:
+ - CLIENT
+ - SERVER
+ target: 2
\ No newline at end of file
diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml
new file mode 100644
index 0000000000..0552a1ce44
--- /dev/null
+++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml
@@ -0,0 +1,40 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+traces:
+ - key: not null
+ endpointNames:
+ - /e2e/info
+ duration: ge 0
+ start: gt 0
+ isError: false
+ traceIds:
+ - not null
+
+ - key: not null
+ endpointNames:
+ - /nginx/e2e/info
+ duration: ge 0
+ start: gt 0
+ isError: false
+ traceIds:
+ - not null
\ No newline at end of file
diff --git a/test/e2e/pom.xml b/test/e2e/pom.xml
index 0a6c8b520c..31790ae6f0 100644
--- a/test/e2e/pom.xml
+++ b/test/e2e/pom.xml
@@ -44,6 +44,7 @@
e2e-6.x-agent-7.x-oap-compatibilitye2e-profilee2e-protocol
+ e2e-http-api-with-nginx-lua
diff --git a/tools/dependencies/known-oap-backend-dependencies-es7.txt b/tools/dependencies/known-oap-backend-dependencies-es7.txt
index 59c3f8df44..2f20ac243b 100755
--- a/tools/dependencies/known-oap-backend-dependencies-es7.txt
+++ b/tools/dependencies/known-oap-backend-dependencies-es7.txt
@@ -142,7 +142,8 @@ opencensus-contrib-grpc-metrics-0.24.0.jar
parent-join-client-7.0.0.jar
perfmark-api-0.19.0.jar
proto-google-common-protos-1.12.0.jar
-protobuf-java-3.4.0.jar
+protobuf-java-3.11.4.jar
+protobuf-java-util-3.11.4.jar
rank-eval-client-7.0.0.jar
reactive-streams-1.0.2.jar
reflectasm-1.11.7.jar
diff --git a/tools/dependencies/known-oap-backend-dependencies.txt b/tools/dependencies/known-oap-backend-dependencies.txt
index 836cced4b0..8b9e85415f 100755
--- a/tools/dependencies/known-oap-backend-dependencies.txt
+++ b/tools/dependencies/known-oap-backend-dependencies.txt
@@ -140,7 +140,8 @@ opencensus-contrib-grpc-metrics-0.24.0.jar
parent-join-client-6.3.2.jar
perfmark-api-0.19.0.jar
proto-google-common-protos-1.12.0.jar
-protobuf-java-3.4.0.jar
+protobuf-java-3.11.4.jar
+protobuf-java-util-3.11.4.jar
rank-eval-client-6.3.2.jar
reactive-streams-1.0.2.jar
reflectasm-1.11.7.jar