diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 20e2bae6d2..e2078dd4ab 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,7 +42,7 @@ jobs: build: name: Java ${{ matrix.java-version }} / ${{ matrix.os }} runs-on: ${{ matrix.os }}-latest - timeout-minutes: 60 + timeout-minutes: 90 needs: [ license ] strategy: fail-fast: true diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 0932d63cee..1abc057ad0 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -54,11 +54,11 @@ jobs: java-version: 17 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - run: ./mvnw -q -Dmaven.test.skip=true clean install || ./mvnw -q -Dmaven.test.skip=true clean install - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/plugins-jdk17-test.1.yaml b/.github/workflows/plugins-jdk17-test.1.yaml index 949e69cfa1..80ff29bb67 100644 --- a/.github/workflows/plugins-jdk17-test.1.yaml +++ b/.github/workflows/plugins-jdk17-test.1.yaml @@ -63,6 +63,7 @@ jobs: - c3p0-0.9.0.x-0.9.1.x-scenario - c3p0-0.9.2.x-0.10.x-scenario - spring-scheduled-6.x-scenario + - caffeine-3.x-scenario steps: - uses: actions/checkout@v2 with: diff --git a/CHANGES.md b/CHANGES.md index 5e959990cd..8a74479321 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -25,6 +25,7 @@ Release Notes. Plugin, Kotlin Coroutine Plugin, and Spring Gateway Plugin * Change context and parent entry span propagation mechanism from gRPC ThreadLocal context to SkyWalking native dynamic field as new propagation mechanism, to better support async scenarios. +* Add Caffeine plugin as optional. All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/222?closed=1) 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 101abcdb5e..e10b207ce5 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 @@ -259,4 +259,5 @@ public class ComponentsDefine { public static final OfficialComponent SOLON_MVC = new OfficialComponent(158, "SolonMVC"); + public static final OfficialComponent CAFFEINE = new OfficialComponent(160, "Caffeine"); } diff --git a/apm-sniffer/config/agent.config b/apm-sniffer/config/agent.config index 695e47349f..f27572a1e0 100755 --- a/apm-sniffer/config/agent.config +++ b/apm-sniffer/config/agent.config @@ -332,3 +332,7 @@ plugin.solon.http_params_length_threshold=${SW_PLUGIN_SOLON_HTTP_PARAMS_LENGTH_T plugin.solon.include_http_headers=${SW_PLUGIN_SOLON_INCLUDE_HTTP_HEADERS:} # Define the max length of collected HTTP body. The default value(=0) means not collecting. plugin.solon.http_body_length_threshold=${SW_PLUGIN_SOLON_HTTP_BODY_LENGTH_THRESHOLD:0} +# Specify which command should be converted to write operation +plugin.caffeine.operation_mapping_write=${SW_PLUGIN_CAFFEINE_OPERATION_MAPPING_WRITE:put,putAll,remove,clear} +# Specify which command should be converted to read operation +plugin.caffeine.operation_mapping_read=${SW_PLUGIN_CAFFEINE_OPERATION_MAPPING_READ:getIfPresent,getAllPresent,computeIfAbsent} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml new file mode 100644 index 0000000000..3170ed81a2 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml @@ -0,0 +1,46 @@ + + + + + 4.0.0 + + org.apache.skywalking + optional-plugins + 9.4.0-SNAPSHOT + + + apm-caffeine-3.x-plugin + jar + caffeine-3.x-plugin + + + UTF-8 + 3.1.8 + + + + + com.github.ben-manes.caffeine + caffeine + ${caffeine.version} + provided + + + diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java new file mode 100644 index 0000000000..9c66cabd08 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.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.plugin.caffeine.v3; + +import java.lang.reflect.Method; +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 static org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineOperationTransform.transformOperation; + +abstract public class AbstractCaffeineInterceptor implements InstanceMethodsAroundInterceptor { + + protected AbstractSpan generateSpanInfo(String methodName) { + AbstractSpan span = ContextManager.createLocalSpan("Caffeine/" + methodName); + span.setComponent(ComponentsDefine.CAFFEINE); + Tags.CACHE_TYPE.set(span, ComponentsDefine.CAFFEINE.getName()); + Tags.CACHE_CMD.set(span, methodName); + transformOperation(methodName).ifPresent(op -> Tags.CACHE_OP.set(span, op)); + SpanLayer.asCache(span); + return span; + } + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + } + + @Override + public Object afterMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java new file mode 100644 index 0000000000..ef5860e46f --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.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.apm.plugin.caffeine.v3; + +import java.lang.reflect.Method; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +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.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +public class CaffeineIterableInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0) { + String keys = StreamSupport + .stream(((Iterable) allArguments[0]).spliterator(), false) + .map(String::valueOf) + .collect(Collectors.joining(",")); + Tags.CACHE_KEY.set(span, keys); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java new file mode 100644 index 0000000000..98258bbb38 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java @@ -0,0 +1,45 @@ +/* + * 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.caffeine.v3; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.stream.Collectors; +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.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +public class CaffeineMapInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0) { + String keys = ((Map) allArguments[0]) + .keySet().stream().map(String::valueOf) + .collect(Collectors.joining(",")); + Tags.CACHE_KEY.set(span, keys); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java new file mode 100644 index 0000000000..0f4a128a92 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java @@ -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. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import java.util.Optional; + +public class CaffeineOperationTransform { + + public static Optional transformOperation(String cmd) { + if (CaffeinePluginConfig.Plugin.Caffeine.OPERATION_MAPPING_READ.contains(cmd)) { + return Optional.of("read"); + } + if (CaffeinePluginConfig.Plugin.Caffeine.OPERATION_MAPPING_WRITE.contains(cmd)) { + return Optional.of("write"); + } + return Optional.empty(); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java new file mode 100644 index 0000000000..f775668bee --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.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.caffeine.v3; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +/** + * Operation represent a cache span is "write" or "read" action , and "op"(operation) is tagged with key "cache.op" + * usually This config term define which command should be converted to write Operation . + * + * @see org.apache.skywalking.apm.agent.core.context.tag.Tags#CACHE_OP + * @see CaffeineOperationTransform#transformOperation(String) + */ +public class CaffeinePluginConfig { + public static class Plugin { + @PluginConfig(root = CaffeinePluginConfig.class) + public static class Caffeine { + public static Set OPERATION_MAPPING_WRITE = new HashSet<>(Arrays.asList( + "put", + "putAll", + "remove", + "clear" + )); + public static Set OPERATION_MAPPING_READ = new HashSet<>(Arrays.asList( + "getIfPresent", + "getAllPresent", + "computeIfAbsent" + )); + } + } +} \ No newline at end of file diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java new file mode 100644 index 0000000000..82b34ca48d --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.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.plugin.caffeine.v3; + +import java.lang.reflect.Method; +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.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +public class CaffeineStringInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0 && allArguments[0] instanceof String) { + Tags.CACHE_KEY.set(span, allArguments[0].toString()); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java new file mode 100644 index 0000000000..50e5b3751a --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java @@ -0,0 +1,119 @@ +/* + * 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.caffeine.v3.define; + +import java.util.Map; +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 net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch.byMultiClassMatch; + +public class CaffeineInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + public static final String BOUNDED_LOCAL_INTERCEPT_CLASS = "com.github.benmanes.caffeine.cache.BoundedLocalCache"; + public static final String UNBOUNDED_LOCAL_INTERCEPT_CLASS = "com.github.benmanes.caffeine.cache.UnboundedLocalCache"; + public static final String CAFFEINE_ITERABLE_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineIterableInterceptor"; + public static final String CAFFEINE_MAP_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineMapInterceptor"; + public static final String CAFFEINE_STRING_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineStringInterceptor"; + + // read/write operations + public static final String GET_IF_PRESENT_METHOD = "getIfPresent"; + public static final String GET_ALL_PRESENT_METHOD = "getAllPresent"; + public static final String COMPUTE_IF_ABSENT_METHOD = "computeIfAbsent"; + public static final String PUT_METHOD = "put"; + public static final String PUT_ALL_METHOD = "putAll"; + public static final String REMOVE_METHOD = "remove"; + public static final String CLEAR_METHOD = "clear"; + + @Override + protected ClassMatch enhanceClass() { + return byMultiClassMatch(BOUNDED_LOCAL_INTERCEPT_CLASS, UNBOUNDED_LOCAL_INTERCEPT_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(GET_IF_PRESENT_METHOD) + .and(takesArguments(2)) + .or(named(COMPUTE_IF_ABSENT_METHOD).and(takesArguments(4))) + .or(named(PUT_METHOD).and(takesArguments(2))) + .or(named(REMOVE_METHOD).and(takesArguments(1))) + .or(named(CLEAR_METHOD).and(takesArguments(0))); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_STRING_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(GET_ALL_PRESENT_METHOD).and(takesArgument(0, Iterable.class)); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_ITERABLE_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(PUT_ALL_METHOD).and(takesArgument(0, Map.class)); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_MAP_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..1edf1e3a0e --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-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. + +caffeine-3.x=org.apache.skywalking.apm.plugin.caffeine.v3.define.CaffeineInstrumentation diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java new file mode 100644 index 0000000000..7642ae110d --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java @@ -0,0 +1,149 @@ +/* + * 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.caffeine.v3; + +import com.github.benmanes.caffeine.cache.Expiry; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +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.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static org.hamcrest.CoreMatchers.is; + +@RunWith(TracingSegmentRunner.class) +public class CaffeineInterceptorTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + private CaffeineIterableInterceptor caffeineIterableInterceptor; + private CaffeineMapInterceptor caffeineMapInterceptor; + private CaffeineStringInterceptor caffeineStringInterceptor; + private Object[] operateObjectArguments; + + private Exception exception; + + private Method getAllPresentMethod; + private Method getIfPresentMethod; + private Method computeIfAbsentMethod; + + private Method putMethod; + private Method putAllMethod; + private Method removeMethod; + private Method cleanMethod; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Before + public void setUp() throws Exception { + caffeineIterableInterceptor = new CaffeineIterableInterceptor(); + caffeineMapInterceptor = new CaffeineMapInterceptor(); + caffeineStringInterceptor = new CaffeineStringInterceptor(); + exception = new Exception(); + operateObjectArguments = new Object[] {"dataKey"}; + Class cache = Class.forName("com.github.benmanes.caffeine.cache.BoundedLocalCache"); + getAllPresentMethod = cache.getDeclaredMethod("getAllPresent", Iterable.class); + getIfPresentMethod = cache.getDeclaredMethod("getIfPresent", Object.class, boolean.class); + computeIfAbsentMethod = cache.getDeclaredMethod( + "computeIfAbsent", Object.class, Function.class, boolean.class, boolean.class); + putMethod = cache.getDeclaredMethod("put", Object.class, Object.class, Expiry.class, boolean.class); + putAllMethod = cache.getDeclaredMethod("putAll", Map.class); + removeMethod = cache.getDeclaredMethod("remove", Object.class); + cleanMethod = cache.getDeclaredMethod("clear"); + } + + @Test + public void testGetAllPresentSuccess() throws Throwable { + caffeineIterableInterceptor.beforeMethod(null, getAllPresentMethod, null, null, null); + caffeineIterableInterceptor.handleMethodException(null, getAllPresentMethod, null, null, exception); + caffeineIterableInterceptor.afterMethod(null, getAllPresentMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testGetIfPresentSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException( + null, getIfPresentMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testComputeIfAbsentMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, computeIfAbsentMethod, null, null, null); + caffeineStringInterceptor.handleMethodException(null, computeIfAbsentMethod, null, null, exception); + caffeineStringInterceptor.afterMethod(null, computeIfAbsentMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testPutMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, putMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException(null, putMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, putMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testPutAllMethodSuccess() throws Throwable { + caffeineMapInterceptor.beforeMethod(null, putAllMethod, null, null, null); + caffeineMapInterceptor.handleMethodException(null, putAllMethod, null, null, exception); + caffeineMapInterceptor.afterMethod(null, putAllMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testRemoveMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, removeMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException(null, removeMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, removeMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testClearMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, cleanMethod, null, null, null); + caffeineStringInterceptor.handleMethodException(null, cleanMethod, null, null, exception); + caffeineStringInterceptor.afterMethod(null, cleanMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..1f0955d450 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline diff --git a/apm-sniffer/optional-plugins/pom.xml b/apm-sniffer/optional-plugins/pom.xml index 4a55525da8..6369484e82 100644 --- a/apm-sniffer/optional-plugins/pom.xml +++ b/apm-sniffer/optional-plugins/pom.xml @@ -59,6 +59,7 @@ trace-sampler-cpu-policy-plugin nacos-client-2.x-plugin netty-http-4.1.x-plugin + caffeine-3.x-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 2c7b51a6c3..847a81dbec 100644 --- a/docs/en/setup/service-agent/java-agent/Plugin-list.md +++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md @@ -179,3 +179,4 @@ - activemq-artemis-jakarta-client-2.x - c3p0-0.9.x - solon-2.x +- caffeine-3.x 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 fe3bcd231d..cd51b8d2ce 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -139,8 +139,9 @@ metrics based on the tracing data. * JRE Callable and Runnable (Optional²) * JRE ForkJoinPool (Optional²) * Cache - * [Ehcache](https://www.ehcache.org/) 2.x + * [Ehcache](https://www.ehcache.org/) 2.x (Optional²) * [GuavaCache](https://github.com/google/guava) 18.x -> 23.x (Optional²) + * [Caffeine](https://github.com/ben-manes/caffeine) 3.x (Optional²) * Kotlin * [Coroutine](https://kotlinlang.org/docs/coroutines-overview.html) 1.0.1 -> 1.3.x (Optional²) * GraphQL diff --git a/docs/en/setup/service-agent/java-agent/configurations.md b/docs/en/setup/service-agent/java-agent/configurations.md index 83837d33b7..9c6cc2d7f6 100644 --- a/docs/en/setup/service-agent/java-agent/configurations.md +++ b/docs/en/setup/service-agent/java-agent/configurations.md @@ -126,17 +126,18 @@ This is the properties list supported in `agent/config/agent.config`. | `plugin.memcached.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_MEMCACHED_OPERATION_MAPPING_READ | `set,add,replace,append,prepend,cas,delete,touch,incr,decr` | | `plugin.ehcache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_WRITE | `get,getAll,getQuiet,getKeys,getKeysWithExpiryCheck,getKeysNoDuplicateCheck,releaseRead,tryRead,getWithLoader,getAll,loadAll,getAllWithLoader` | | `plugin.ehcache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_READ | `tryRemoveImmediately,remove,removeAndReturnElement,removeAll,removeQuiet,removeWithWriter,put,putAll,replace,removeQuiet,removeWithWriter,removeElement,removeAll,putWithWriter,putQuiet,putIfAbsent,putIfAbsent` | -| `plugin.guavacache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_WRITE | `getIfPresent,get,getAllPresent,size` | -| `plugin.guavacache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_READ | `put,putAll,invalidate,invalidateAll,invalidateAll,cleanUp` -| `plugin.nettyhttp.collect_request_body` | This config item controls that whether the Netty-http plugin should collect the http body of the request. | SW_PLUGIN_NETTY_HTTP_COLLECT_REQUEST_BODY | `false` | -| `plugin.nettyhttp.filter_length_limit` | When `COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. | SW_PLUGIN_NETTY_HTTP_FILTER_LENGTH_LIMIT | `1024` | -| `plugin.nettyhttp.supported_content_types_prefix` | When `COLLECT_REQUEST_BODY` is enabled and content-type start with `HTTP_SUPPORTED_CONTENT_TYPES_PREFIX`, collect the body of the request , multiple paths should be separated by `,` | SW_PLUGIN_NETTY_HTTP_SUPPORTED_CONTENT_TYPES_PREFIX | `application/json,text/` | -| `plugin.rocketmqclient.collect_message_keys` | If set to true, the keys of messages would be collected by the plugin for RocketMQ Java client. -| `plugin.rocketmqclient.collect_message_tags` | If set to true, the tags of messages would be collected by the plugin for RocketMQ Java client. -| `plugin.solon.http_params_length_threshold` | Define the max length of collected HTTP parameters. The default value(=0) means not collecting. | SW_PLUGIN_SOLON_HTTP_PARAMS_LENGTH_THRESHOLD | `0` | -| `plugin.solon.include_http_headers` | It controls what header data should be collected, values must be in lower case, if empty, no header data will be collected. default is empty. | SW_PLUGIN_SOLON_INCLUDE_HTTP_HEADERS | ``(No header would be collected) | -| `plugin.solon.http_body_length_threshold` | Define the max length of collected HTTP body. The default value(=0) means not collecting. | SW_PLUGIN_SOLON_HTTP_BODY_LENGTH_THRESHOLD | `0` | - +| `plugin.guavacache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_WRITE | `put,putAll,invalidate,invalidateAll,invalidateAll,cleanUp` | +| `plugin.guavacache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_READ | `getIfPresent,get,getAllPresent,size` | +| `plugin.nettyhttp.collect_request_body` | This config item controls that whether the Netty-http plugin should collect the http body of the request. | SW_PLUGIN_NETTY_HTTP_COLLECT_REQUEST_BODY | `false` | +| `plugin.nettyhttp.filter_length_limit` | When `COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. | SW_PLUGIN_NETTY_HTTP_FILTER_LENGTH_LIMIT | `1024` | +| `plugin.nettyhttp.supported_content_types_prefix` | When `COLLECT_REQUEST_BODY` is enabled and content-type start with `HTTP_SUPPORTED_CONTENT_TYPES_PREFIX`, collect the body of the request , multiple paths should be separated by `,` | SW_PLUGIN_NETTY_HTTP_SUPPORTED_CONTENT_TYPES_PREFIX | `application/json,text/` | +| `plugin.rocketmqclient.collect_message_keys` | If set to true, the keys of messages would be collected by the plugin for RocketMQ Java client. | | | +| `plugin.rocketmqclient.collect_message_tags` | If set to true, the tags of messages would be collected by the plugin for RocketMQ Java client. | | | +| `plugin.solon.http_params_length_threshold` | Define the max length of collected HTTP parameters. The default value(=0) means not collecting. | SW_PLUGIN_SOLON_HTTP_PARAMS_LENGTH_THRESHOLD | `0` | +| `plugin.solon.include_http_headers` | It controls what header data should be collected, values must be in lower case, if empty, no header data will be collected. default is empty. | SW_PLUGIN_SOLON_INCLUDE_HTTP_HEADERS | ``(No header would be collected) | +| `plugin.solon.http_body_length_threshold` | Define the max length of collected HTTP body. The default value(=0) means not collecting. | SW_PLUGIN_SOLON_HTTP_BODY_LENGTH_THRESHOLD | `0` | +| `plugin.caffeine.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_WRITE | `put,putAll,remove,clear` | +| `plugin.caffeine.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_READ | `getIfPresent,getAllPresent,computeIfAbsent` | # Reset Collection/Map type configurations as empty collection. diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh b/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh new file mode 100755 index 0000000000..01883d186d --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# 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. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/caffeine-3.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml new file mode 100755 index 0000000000..133ede4755 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml @@ -0,0 +1,163 @@ +# 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: caffeine-3.x-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: Caffeine/put + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: put } + - { key: cache.op, value: write } + - { key: cache.key, value: "1" } + skipAnalysis: 'false' + - operationName: Caffeine/putAll + parentSpanId: 0 + spanId: 2 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: putAll } + - { key: cache.op, value: write } + - { key: cache.key, value: "2" } + skipAnalysis: 'false' + - operationName: Caffeine/computeIfAbsent + parentSpanId: 0 + spanId: 3 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: computeIfAbsent } + - { key: cache.op, value: read } + - { key: cache.key, value: "1" } + - operationName: Caffeine/getIfPresent + parentSpanId: 0 + spanId: 4 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: getIfPresent } + - { key: cache.op, value: read } + - { key: cache.key, value: "1" } + skipAnalysis: 'false' + - operationName: Caffeine/getAllPresent + parentSpanId: 0 + spanId: 5 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: getAllPresent } + - { key: cache.op, value: read } + - { key: cache.key, value: '2' } + - operationName: Caffeine/remove + parentSpanId: 0 + spanId: 6 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: remove } + - { key: cache.op, value: write } + - { key: cache.key, value: '1' } + - operationName: Caffeine/remove + parentSpanId: 0 + spanId: 7 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: remove } + - { key: cache.op, value: write } + - { key: cache.key, value: '2' } + - operationName: Caffeine/clear + parentSpanId: 0 + spanId: 8 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: clear } + - { key: cache.op, value: write } + - operationName: GET:/caffeine-3.x-scenario/case/caffeine + 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/caffeine-3.x-scenario/case/caffeine' } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml b/test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml new file mode 100755 index 0000000000..00208eefa9 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/configuration.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. + +type: jvm +entryService: http://localhost:8080/caffeine-3.x-scenario/case/caffeine +healthCheck: http://localhost:8080/caffeine-3.x-scenario/case/healthCheck +startScript: ./bin/startup.sh +runningMode: with_optional +withPlugins: apm-caffeine-3.x-plugin-*.jar \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml b/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml new file mode 100755 index 0000000000..a627109082 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml @@ -0,0 +1,111 @@ + + + + + org.apache.skywalking.apm.testcase + caffeine-3.x-scenario + 1.0.0 + jar + + 4.0.0 + + + 17 + UTF-8 + 3.8.1 + 6.0.2 + ${test.framework.version} + 3.0.0 + 3.1.8 + + + skywalking-caffeine-3.x-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + com.github.ben-manes.caffeine + caffeine + ${caffeine.version} + + + + + caffeine-3.x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml new file mode 100755 index 0000000000..382a68db61 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/caffeine-3.x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java new file mode 100644 index 0000000000..a4739cfbfe --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java @@ -0,0 +1,30 @@ +/* + * 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.caffeine; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java new file mode 100644 index 0000000000..f8bad1d288 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java @@ -0,0 +1,56 @@ +/* + * 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.caffeine.controller; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.HashMap; +import java.util.Map; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CaffeineController { + + Cache caffeine = Caffeine.newBuilder().build(); + + @GetMapping("/case/caffeine") + public void testCase() { + try { + Map data = new HashMap<>(); + data.put("2", "value-2"); + + // put operations + caffeine.put("1", "value-1"); + caffeine.putAll(data); + + // get operations + caffeine.get("1", o -> "default"); + caffeine.getIfPresent("1"); + caffeine.getAllPresent(data.keySet()); + + // delete operations + caffeine.invalidate("1"); + caffeine.invalidateAll(data.keySet()); + caffeine.invalidateAll(); + } catch (Exception e) { + throw e; + } + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java new file mode 100644 index 0000000000..9dbc01dc77 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java @@ -0,0 +1,31 @@ +/* + * 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.caffeine.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HeartbeatController { + + @GetMapping("/case/healthCheck") + public String healthCheck() { + return "success"; + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml new file mode 100755 index 0000000000..ac1fb9a734 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml @@ -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. +# +# +server: + port: 8080 + servlet: + context-path: /caffeine-3.x-scenario \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/support-version.list b/test/plugin/scenarios/caffeine-3.x-scenario/support-version.list new file mode 100755 index 0000000000..0c44c26905 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/support-version.list @@ -0,0 +1,18 @@ +# 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. + +3.0.0 +3.1.8 \ No newline at end of file