diff --git a/.github/workflows/plugins-test.0.yaml b/.github/workflows/plugins-test.0.yaml
index 9c8ac3f94..5356fc617 100644
--- a/.github/workflows/plugins-test.0.yaml
+++ b/.github/workflows/plugins-test.0.yaml
@@ -71,6 +71,7 @@ jobs:
- gateway-2.0.x-scenario
- grpc-scenario
- gson-scenario
+ - guava-cache-scenario
- elasticjob-3.x-scenario
- springmvc-reactive-scenario
- springmvc-reactive-devtools-scenario
diff --git a/CHANGES.md b/CHANGES.md
index 3aeba6339..21d6cb0de 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -29,6 +29,7 @@ Release Notes.
* Fix async finish repeatedly in `spring-webflux-5.x-webclient` plugin.
* Add agent plugin to support Sentinel.
* Move `ehcache-2.x` plugin as an optional plugin.
+* Support `guava-cache` plugin
#### OAP-Backend
diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
index 2fc8cb538..8fb8ddb38 100755
--- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
+++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
@@ -204,4 +204,7 @@ public class ComponentsDefine {
public static final OfficialComponent NEO4J = new OfficialComponent(112, "Neo4j");
public static final OfficialComponent SENTINEL = new OfficialComponent(113, "Sentinel");
+
+ public static final OfficialComponent GUAVA_CACHE = new OfficialComponent(114, "GuavaCache");
+
}
diff --git a/apm-sniffer/optional-plugins/guava-cache-plugin/pom.xml b/apm-sniffer/optional-plugins/guava-cache-plugin/pom.xml
new file mode 100644
index 000000000..15528eec1
--- /dev/null
+++ b/apm-sniffer/optional-plugins/guava-cache-plugin/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+ org.apache.skywalking
+ optional-plugins
+ 8.7.0-SNAPSHOT
+
+
+ apm-guava-cache-plugin
+ guava-cache-plugin
+ jar
+
+ 4.0.0
+
+
+ 23.0
+
+
+
+
+
+ com.google.guava
+ guava
+ ${guava-client.version}
+ provided
+
+
+
+
+
\ No newline at end of file
diff --git a/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheAllInterceptor.java b/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheAllInterceptor.java
new file mode 100644
index 000000000..fb945a199
--- /dev/null
+++ b/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheAllInterceptor.java
@@ -0,0 +1,50 @@
+/*
+ * 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.apm.plugin.guava.cache;
+
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
+
+import java.lang.reflect.Method;
+
+public class GuavaCacheAllInterceptor implements InstanceMethodsAroundInterceptor {
+
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+ AbstractSpan span = ContextManager.createLocalSpan("GuavaCache/" + method.getName());
+ span.setComponent(ComponentsDefine.GUAVA_CACHE);
+ SpanLayer.asCache(span);
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Object ret) throws Throwable {
+ ContextManager.stopSpan();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Throwable t) {
+ ContextManager.activeSpan().log(t);
+ }
+}
diff --git a/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheInterceptor.java b/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheInterceptor.java
new file mode 100644
index 000000000..05d3bbabb
--- /dev/null
+++ b/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheInterceptor.java
@@ -0,0 +1,55 @@
+/*
+ * 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.apm.plugin.guava.cache;
+
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.tag.Tags;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
+
+import java.lang.reflect.Method;
+
+public class GuavaCacheInterceptor implements InstanceMethodsAroundInterceptor {
+
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+ AbstractSpan span = ContextManager.createLocalSpan("GuavaCache/" + method.getName());
+ span.setComponent(ComponentsDefine.GUAVA_CACHE);
+ Object key = allArguments[0];
+ if (key != null) {
+ Tags.DB_STATEMENT.set(span, key.toString());
+ }
+ SpanLayer.asCache(span);
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Object ret) throws Throwable {
+ ContextManager.stopSpan();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Throwable t) {
+ ContextManager.activeSpan().log(t);
+ }
+}
diff --git a/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/define/GuavaCachePluginInstrumentation.java b/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/define/GuavaCachePluginInstrumentation.java
new file mode 100644
index 000000000..4ac151565
--- /dev/null
+++ b/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/java/org/apache/skywalking/apm/plugin/guava/cache/define/GuavaCachePluginInstrumentation.java
@@ -0,0 +1,99 @@
+/*
+ * 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.apm.plugin.guava.cache.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch.byMultiClassMatch;
+
+public class GuavaCachePluginInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+ public static final String INTERCEPT_LOCALMANUAL_CLASS = "com.google.common.cache.LocalCache$LocalManualCache";
+ public static final String INTERCEPT_LOCALLOADING_CLASS = "com.google.common.cache.LocalCache$LocalLoadingCache";
+ public static final String INTERCEPT_FORWARDING_CLASS = "com.google.common.cache.ForwardingCache";
+ public static final String GET_ALL_PRESENT_ENHANCE_METHOD = "getAllPresent";
+ public static final String INVALIDATE_ALL_ENHANCE_METHOD = "invalidateAll";
+ public static final String GET_ENHANCE_METHOD = "get";
+ public static final String INVALIDATE_ENHANCE_METHOD = "invalidate";
+ public static final String PUT_ALL_ENHANCE_METHOD = "putAll";
+ public static final String PUT_ENHANCE_METHOD = "put";
+ public static final String GET_IF_PRESENT_ENHANCE_METHOD = "getIfPresent";
+ public static final String GUAVA_CACHE_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.guava.cache.GuavaCacheInterceptor";
+ public static final String GUAVA_CACHE_ALL_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.guava.cache.GuavaCacheAllInterceptor";
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[0];
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[]{
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named(GET_ENHANCE_METHOD)
+ .or(named(INVALIDATE_ENHANCE_METHOD))
+ .or(named(PUT_ENHANCE_METHOD))
+ .or(named(GET_IF_PRESENT_ENHANCE_METHOD));
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return GUAVA_CACHE_INTERCEPTOR_CLASS;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return true;
+ }
+
+ },
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named(GET_ALL_PRESENT_ENHANCE_METHOD)
+ .or(named(INVALIDATE_ALL_ENHANCE_METHOD))
+ .or(named(PUT_ALL_ENHANCE_METHOD));
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return GUAVA_CACHE_ALL_INTERCEPTOR_CLASS;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return true;
+ }
+
+ }
+ };
+ }
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return byMultiClassMatch(INTERCEPT_LOCALMANUAL_CLASS, INTERCEPT_LOCALLOADING_CLASS, INTERCEPT_FORWARDING_CLASS);
+ }
+}
diff --git a/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 000000000..7c9d7dae2
--- /dev/null
+++ b/apm-sniffer/optional-plugins/guava-cache-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1,17 @@
+# 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.
+
+guava-cache=org.apache.skywalking.apm.plugin.guava.cache.define.GuavaCachePluginInstrumentation
\ No newline at end of file
diff --git a/apm-sniffer/optional-plugins/guava-cache-plugin/src/test/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheInterceptorTest.java b/apm-sniffer/optional-plugins/guava-cache-plugin/src/test/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheInterceptorTest.java
new file mode 100644
index 000000000..2930ef7b1
--- /dev/null
+++ b/apm-sniffer/optional-plugins/guava-cache-plugin/src/test/java/org/apache/skywalking/apm/plugin/guava/cache/GuavaCacheInterceptorTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.apm.plugin.guava.cache;
+
+import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
+import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStorage;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint;
+import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+import org.powermock.reflect.Whitebox;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(TracingSegmentRunner.class)
+public class GuavaCacheInterceptorTest {
+
+ @SegmentStoragePoint
+ private SegmentStorage segmentStorage;
+
+ private GuavaCacheInterceptor guavaCacheInterceptor;
+
+ private GuavaCacheAllInterceptor guavaCacheAllInterceptor;
+
+ private Object[] operateObjectArguments;
+
+ private Exception exception;
+
+ private Method getAllPresentMethod;
+ private Method invalidateAllMethod;
+ private Method getMethod;
+
+ private Method invalidateMethod;
+ private Method putAllMethod;
+ private Method putMethod;
+ private Method getIfPresentMethod;
+
+ @Rule
+ public AgentServiceRule serviceRule = new AgentServiceRule();
+
+ @Before
+ public void setUp() throws Exception {
+ guavaCacheInterceptor = new GuavaCacheInterceptor();
+ guavaCacheAllInterceptor = new GuavaCacheAllInterceptor();
+ exception = new Exception();
+ operateObjectArguments = new Object[]{"dataKey"};
+ Class> cache = Class.forName("com.google.common.cache.LocalCache$LocalManualCache");
+ getAllPresentMethod = Whitebox.getMethods(cache, "getAllPresent")[0];
+ invalidateAllMethod = Whitebox.getMethods(cache, "invalidateAll")[0];
+ getMethod = Whitebox.getMethods(cache, "get")[0];
+ invalidateMethod = Whitebox.getMethods(cache, "invalidate")[0];
+ putAllMethod = Whitebox.getMethods(cache, "putAll")[0];
+ putMethod = Whitebox.getMethods(cache, "put")[0];
+ getIfPresentMethod = Whitebox.getMethods(cache, "getIfPresent")[0];
+
+ }
+
+ @Test
+ public void assertGetAllPresentSuccess() throws Throwable {
+ guavaCacheAllInterceptor.beforeMethod(null, getAllPresentMethod, null, null, null);
+ guavaCacheAllInterceptor.handleMethodException(null, getAllPresentMethod, null, null, exception);
+ guavaCacheAllInterceptor.afterMethod(null, getAllPresentMethod, null, null, null);
+ List traceSegments = segmentStorage.getTraceSegments();
+ Assert.assertThat(traceSegments.size(), is(1));
+ }
+
+ @Test
+ public void assertInvalidAllSuccess() throws Throwable {
+ guavaCacheAllInterceptor.beforeMethod(null, invalidateAllMethod, null, null, null);
+ guavaCacheAllInterceptor.handleMethodException(null, invalidateAllMethod, null, null, exception);
+ guavaCacheAllInterceptor.afterMethod(null, invalidateAllMethod, null, null, null);
+ List traceSegments = segmentStorage.getTraceSegments();
+ Assert.assertThat(traceSegments.size(), is(1));
+ }
+
+ @Test
+ public void assertGetSuccess() throws Throwable {
+ guavaCacheInterceptor.beforeMethod(null, getMethod, operateObjectArguments, null, null);
+ guavaCacheInterceptor.handleMethodException(null, getMethod, operateObjectArguments, null, exception);
+ guavaCacheInterceptor.afterMethod(null, getMethod, operateObjectArguments, null, null);
+ List traceSegments = segmentStorage.getTraceSegments();
+ Assert.assertThat(traceSegments.size(), is(1));
+ }
+
+ @Test
+ public void assertInvalidateSuccess() throws Throwable {
+ guavaCacheInterceptor.beforeMethod(null, invalidateMethod, operateObjectArguments, null, null);
+ guavaCacheInterceptor.handleMethodException(null, invalidateMethod, operateObjectArguments, null, exception);
+ guavaCacheInterceptor.afterMethod(null, invalidateMethod, operateObjectArguments, null, null);
+ List traceSegments = segmentStorage.getTraceSegments();
+ Assert.assertThat(traceSegments.size(), is(1));
+ }
+
+ @Test
+ public void assertPutAllMethodSuccess() throws Throwable {
+ guavaCacheAllInterceptor.beforeMethod(null, putAllMethod, null, null, null);
+ guavaCacheAllInterceptor.handleMethodException(null, putAllMethod, null, null, exception);
+ guavaCacheAllInterceptor.afterMethod(null, putAllMethod, null, null, null);
+ List traceSegments = segmentStorage.getTraceSegments();
+ Assert.assertThat(traceSegments.size(), is(1));
+ }
+
+ @Test
+ public void assertPutMethodSuccess() throws Throwable {
+ guavaCacheInterceptor.beforeMethod(null, putMethod, operateObjectArguments, null, null);
+ guavaCacheInterceptor.handleMethodException(null, putMethod, operateObjectArguments, null, exception);
+ guavaCacheInterceptor.afterMethod(null, putMethod, operateObjectArguments, null, null);
+ List traceSegments = segmentStorage.getTraceSegments();
+ Assert.assertThat(traceSegments.size(), is(1));
+ }
+
+ @Test
+ public void assertGetIfPresentMethodSuccess() throws Throwable {
+ guavaCacheInterceptor.beforeMethod(null, getIfPresentMethod, operateObjectArguments, null, null);
+ guavaCacheInterceptor.handleMethodException(null, getIfPresentMethod, operateObjectArguments, null, exception);
+ guavaCacheInterceptor.afterMethod(null, getIfPresentMethod, operateObjectArguments, null, null);
+ List traceSegments = segmentStorage.getTraceSegments();
+ Assert.assertThat(traceSegments.size(), is(1));
+ }
+}
diff --git a/apm-sniffer/optional-plugins/pom.xml b/apm-sniffer/optional-plugins/pom.xml
index 8f49f27f3..897a05092 100644
--- a/apm-sniffer/optional-plugins/pom.xml
+++ b/apm-sniffer/optional-plugins/pom.xml
@@ -52,6 +52,7 @@
mybatis-3.x-pluginsentinel-1.x-pluginehcache-2.x-plugin
+ guava-cache-plugin
diff --git a/docs/en/setup/service-agent/java-agent/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md
index 1d46c7431..58d5ee5ca 100644
--- a/docs/en/setup/service-agent/java-agent/Plugin-list.md
+++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md
@@ -23,6 +23,7 @@
- graphql
- grpc-1.x
- gson-2.8.x
+- guava-cache
- h2-1.x
- hbase-1.x/2.x
- httpasyncclient-4.x
diff --git a/docs/en/setup/service-agent/java-agent/README.md b/docs/en/setup/service-agent/java-agent/README.md
index c25337adf..d41c926c1 100755
--- a/docs/en/setup/service-agent/java-agent/README.md
+++ b/docs/en/setup/service-agent/java-agent/README.md
@@ -195,6 +195,7 @@ Now, we have the following known optional plugins.
* Plugin of mybatis-3.x in optional plugin folder. The reason of being optional plugin is, many local span are generated, which also spend more CPU, memory and network.
* Plugin of sentinel-1.x in the optional plugin folder. The reason for being an optional plugin is, the sentinel plugin generates a large number of local spans, which have a potential performance impact.
* Plugin of ehcache-2.x in the optional plugin folder. The reason for being an optional plugin is, this plugin enhanced cache framework, generates large number of local spans, which have a potential performance impact.
+* Plugin of guava-cache in the optional plugin folder. The reason for being an optional plugin is, this plugin enhanced cache framework, generates large number of local spans, which have a potential performance impact.
## Bootstrap class plugins
All bootstrap plugins are optional, due to unexpected risk. Bootstrap plugins are provided in `bootstrap-plugins` folder.
diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md
index a5fd41e4d..a08d39cab 100644
--- a/docs/en/setup/service-agent/java-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/java-agent/Supported-list.md
@@ -116,6 +116,7 @@ metrics based on the tracing data.
* JRE Callable and Runnable (Optional²)
* Cache
* [Ehcache](https://www.ehcache.org/) 2.x
+ * [GuavaCache](https://github.com/google/guava) 18.x -> 23.x (Optional²)
* Kotlin
* [Coroutine](https://kotlinlang.org/docs/reference/coroutines-overview.html) 1.0.1 -> 1.3.x (Optional²)
* GraphQL
diff --git a/oap-server/server-bootstrap/src/main/resources/component-libraries.yml b/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
index 5e6e1588c..0d3d112f7 100755
--- a/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
+++ b/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
@@ -371,6 +371,9 @@ Neo4j:
Sentinel:
id: 113
languages: Java
+GuavaCache:
+ id: 114
+ languages: Java
# .NET/.NET Core components
# [3000, 4000) for C#/.NET only
diff --git a/test/plugin/scenarios/guava-cache-scenario/config/expectedData.yaml b/test/plugin/scenarios/guava-cache-scenario/config/expectedData.yaml
new file mode 100644
index 000000000..c33f4d9c6
--- /dev/null
+++ b/test/plugin/scenarios/guava-cache-scenario/config/expectedData.yaml
@@ -0,0 +1,101 @@
+# 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.
+segmentItems:
+- serviceName: guava-cache-scenario
+ segmentSize: ge 1
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: GuavaCache/put
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Cache
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 114
+ isError: false
+ spanType: Local
+ peer: ''
+ tags:
+ - {key: db.statement, value: testKey1}
+ skipAnalysis: 'false'
+ - operationName: GuavaCache/invalidate
+ operationId: 0
+ parentSpanId: 0
+ spanId: 2
+ spanLayer: Cache
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 114
+ isError: false
+ spanType: Local
+ peer: ''
+ tags:
+ - {key: db.statement, value: testKey1}
+ skipAnalysis: 'false'
+ - operationName: GuavaCache/get
+ operationId: 0
+ parentSpanId: 0
+ spanId: 3
+ spanLayer: Cache
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 114
+ isError: false
+ spanType: Local
+ peer: ''
+ tags:
+ - {key: db.statement, value: testKey1}
+ skipAnalysis: 'false'
+ - operationName: GuavaCache/getIfPresent
+ operationId: 0
+ parentSpanId: 0
+ spanId: 4
+ spanLayer: Cache
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 114
+ isError: false
+ spanType: Local
+ peer: ''
+ tags:
+ - {key: db.statement, value: testKey1}
+ skipAnalysis: 'false'
+ - {operationName: GuavaCache/putAll, operationId: 0, parentSpanId: 0, spanId: 5, spanLayer: Cache,
+ startTime: nq 0, endTime: nq 0, componentId: 114, isError: false,
+ spanType: Local, peer: '', skipAnalysis: false}
+ - {operationName: GuavaCache/getAllPresent, operationId: 0, parentSpanId: 0, spanId: 6,
+ spanLayer: Cache, startTime: nq 0, endTime: nq 0, componentId: 114,
+ isError: false, spanType: Local, peer: '', skipAnalysis: false}
+ - {operationName: GuavaCache/invalidateAll, operationId: 0, parentSpanId: 0, spanId: 7,
+ spanLayer: Cache, startTime: nq 0, endTime: nq 0, componentId: 114,
+ isError: false, spanType: Local, peer: '', skipAnalysis: false}
+ - operationName: /guava-cache-scenario/case/guava
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ spanType: Entry
+ peer: ''
+ tags:
+ - {key: url, value: 'http://localhost:8080/guava-cache-scenario/case/guava'}
+ - {key: http.method, value: GET}
+ skipAnalysis: 'false'
diff --git a/test/plugin/scenarios/guava-cache-scenario/configuration.yml b/test/plugin/scenarios/guava-cache-scenario/configuration.yml
new file mode 100644
index 000000000..f6615dbb5
--- /dev/null
+++ b/test/plugin/scenarios/guava-cache-scenario/configuration.yml
@@ -0,0 +1,21 @@
+# 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.
+
+type: tomcat
+entryService: http://localhost:8080/guava-cache-scenario/case/guava
+healthCheck: http://localhost:8080/guava-cache-scenario/healthCheck
+runningMode: with_optional
+withPlugins: apm-guava-cache-plugin-*.jar
diff --git a/test/plugin/scenarios/guava-cache-scenario/pom.xml b/test/plugin/scenarios/guava-cache-scenario/pom.xml
new file mode 100644
index 000000000..b875f7343
--- /dev/null
+++ b/test/plugin/scenarios/guava-cache-scenario/pom.xml
@@ -0,0 +1,77 @@
+
+
+
+ 4.0.0
+
+ org.apache.skywalking
+ guava-cache-scenario
+ 5.0.0
+ war
+
+
+ UTF-8
+ 1.8
+
+
+ guava
+ 23.0
+
+
+ skywalking-guava-cache-scenario
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.1.0
+ provided
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.8.1
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.8.1
+
+
+ com.google.guava
+ guava
+ ${test.framework.version}
+
+
+
+
+ guava-cache-scenario
+
+
+ maven-compiler-plugin
+
+ ${compiler.version}
+ ${compiler.version}
+ ${project.build.sourceEncoding}
+
+
+
+
+
\ No newline at end of file
diff --git a/test/plugin/scenarios/guava-cache-scenario/src/main/java/org/apache/skywalking/apm/testcase/guava/cache/CaseServlet.java b/test/plugin/scenarios/guava-cache-scenario/src/main/java/org/apache/skywalking/apm/testcase/guava/cache/CaseServlet.java
new file mode 100644
index 000000000..e417210ee
--- /dev/null
+++ b/test/plugin/scenarios/guava-cache-scenario/src/main/java/org/apache/skywalking/apm/testcase/guava/cache/CaseServlet.java
@@ -0,0 +1,71 @@
+/*
+ * 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.apm.testcase.guava.cache;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+public class CaseServlet extends HttpServlet {
+
+ Cache cache = CacheBuilder.newBuilder().softValues().build();
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String key1 = "testKey1";
+ String value1 = "testValue1";
+ cache.put(key1, value1);
+ cache.invalidate(key1);
+ try {
+ cache.get(key1, () -> value1);
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ }
+ cache.getIfPresent(key1);
+ Map kvs = Maps.newHashMap();
+ String key2 = "testKey2";
+ String value2 = "testValue2";
+ kvs.put(key2, value2);
+ cache.putAll(kvs);
+ List testKeys = Lists.newArrayList(key1, key2);
+ cache.getAllPresent(testKeys);
+ cache.invalidateAll();
+ PrintWriter printWriter = resp.getWriter();
+ printWriter.write("success");
+ printWriter.flush();
+ printWriter.close();
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ doGet(req, resp);
+ }
+
+}
diff --git a/test/plugin/scenarios/guava-cache-scenario/src/main/java/org/apache/skywalking/apm/testcase/guava/cache/HealthCheckServlet.java b/test/plugin/scenarios/guava-cache-scenario/src/main/java/org/apache/skywalking/apm/testcase/guava/cache/HealthCheckServlet.java
new file mode 100644
index 000000000..5ebfa8bd8
--- /dev/null
+++ b/test/plugin/scenarios/guava-cache-scenario/src/main/java/org/apache/skywalking/apm/testcase/guava/cache/HealthCheckServlet.java
@@ -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.
+ */
+
+package org.apache.skywalking.apm.testcase.guava.cache;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class HealthCheckServlet extends HttpServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ PrintWriter writer = resp.getWriter();
+ writer.write("Success");
+ writer.flush();
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ doGet(req, resp);
+ }
+}
diff --git a/test/plugin/scenarios/guava-cache-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/guava-cache-scenario/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..4c6c06d40
--- /dev/null
+++ b/test/plugin/scenarios/guava-cache-scenario/src/main/resources/log4j2.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/plugin/scenarios/guava-cache-scenario/src/main/webapp/WEB-INF/web.xml b/test/plugin/scenarios/guava-cache-scenario/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 000000000..539fef6a1
--- /dev/null
+++ b/test/plugin/scenarios/guava-cache-scenario/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,44 @@
+
+
+ skywalking-guava-cache-scenario
+
+
+ caseServlet
+ org.apache.skywalking.apm.testcase.guava.cache.CaseServlet
+
+
+ caseServlet
+ /case/guava
+
+
+
+ healthCheck
+ org.apache.skywalking.apm.testcase.guava.cache.HealthCheckServlet
+
+
+ healthCheck
+ /healthCheck
+
+
+
+
diff --git a/test/plugin/scenarios/guava-cache-scenario/support-version.list b/test/plugin/scenarios/guava-cache-scenario/support-version.list
new file mode 100644
index 000000000..41e6e7502
--- /dev/null
+++ b/test/plugin/scenarios/guava-cache-scenario/support-version.list
@@ -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.
+
+18.0
+19.0
+20.0
+21.0
+22.0
+23.0
\ No newline at end of file