未验证 提交 b732ff35 编写于 作者: L Lu Jiajing 提交者: GitHub

Fix ClassCast issue for RequestHolder/ResponseHolder (#6973)

上级 96082acc
...@@ -69,6 +69,7 @@ jobs: ...@@ -69,6 +69,7 @@ jobs:
- gson-scenario - gson-scenario
- elasticjob-3.x-scenario - elasticjob-3.x-scenario
- springmvc-reactive-scenario - springmvc-reactive-scenario
- springmvc-reactive-devtools-scenario
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
......
...@@ -30,6 +30,7 @@ Release Notes. ...@@ -30,6 +30,7 @@ Release Notes.
* Fix the conversion problem of float type in ConfigInitializer. * Fix the conversion problem of float type in ConfigInitializer.
* Fixed part of the dynamic configuration of ConfigurationDiscoveryService that does not take effect under certain circumstances. * Fixed part of the dynamic configuration of ConfigurationDiscoveryService that does not take effect under certain circumstances.
* Introduce method interceptor API v2 * Introduce method interceptor API v2
* Fix ClassCast issue for RequestHolder/ResponseHolder.
#### OAP-Backend #### OAP-Backend
* BugFix: filter invalid Envoy access logs whose socket address is empty. * BugFix: filter invalid Envoy access logs whose socket address is empty.
......
/*
* 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.agent.core.context;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.GCProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class ContextManagerBenchmark {
@Benchmark
@Fork(value = 1, warmups = 1)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.SampleTime)
public void getKeyFromThreadLocal(Blackhole bh) {
bh.consume(ContextManager.getRuntimeContext().get("KEY"));
}
@Benchmark
@Fork(value = 1, warmups = 1)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.SampleTime)
public void isAssignableFrom(Blackhole bh) {
bh.consume(Map.class.isAssignableFrom(HashMap.class));
}
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder().include(ContextManagerBenchmark.class.getSimpleName())
.addProfiler(GCProfiler.class)
.build();
new Runner(opt).run();
}
/**
* # JMH version: 1.21
* # VM version: JDK 1.8.0_292, OpenJDK 64-Bit Server VM, 25.292-b10
* # VM invoker: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/bin/java
* # VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=62459:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
* # Warmup: 5 iterations, 10 s each
* # Measurement: 5 iterations, 10 s each
* # Timeout: 10 min per iteration
* # Threads: 1 thread, will synchronize iterations
* # Benchmark mode: Sampling time
*
* Benchmark Mode Cnt Score Error Units
* ContextManagerBenchmark.getKeyFromThreadLocal sample 1332415 48.066 ± 0.765 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.00 sample 11.000 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.50 sample 42.000 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.90 sample 45.000 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.95 sample 47.000 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.99 sample 93.000 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.999 sample 652.000 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p0.9999 sample 11236.403 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:getKeyFromThreadLocal·p1.00 sample 144384.000 ns/op
* ContextManagerBenchmark.getKeyFromThreadLocal:·gc.alloc.rate sample 5 0.022 ± 0.010 MB/sec
* ContextManagerBenchmark.getKeyFromThreadLocal:·gc.alloc.rate.norm sample 5 ≈ 10⁻⁴ B/op
* ContextManagerBenchmark.getKeyFromThreadLocal:·gc.count sample 5 ≈ 0 counts
* ContextManagerBenchmark.isAssignableFrom sample 1182629 45.894 ± 0.939 ns/op
* ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.00 sample 7.000 ns/op
* ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.50 sample 39.000 ns/op
* ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.90 sample 43.000 ns/op
* ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.95 sample 46.000 ns/op
* ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.99 sample 89.000 ns/op
* ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.999 sample 606.000 ns/op
* ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p0.9999 sample 15904.000 ns/op
* ContextManagerBenchmark.isAssignableFrom:isAssignableFrom·p1.00 sample 138240.000 ns/op
* ContextManagerBenchmark.isAssignableFrom:·gc.alloc.rate sample 5 0.021 ± 0.007 MB/sec
* ContextManagerBenchmark.isAssignableFrom:·gc.alloc.rate.norm sample 5 ≈ 10⁻⁴ B/op
* ContextManagerBenchmark.isAssignableFrom:·gc.count sample 5 ≈ 0 counts
*/
}
...@@ -19,12 +19,10 @@ ...@@ -19,12 +19,10 @@
package org.apache.skywalking.apm.plugin.spring.mvc.v3; package org.apache.skywalking.apm.plugin.spring.mvc.v3;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.servlet.http.HttpServletResponse;
import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; 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.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
...@@ -40,8 +38,7 @@ public class HandlerMethodInvokerInterceptor implements InstanceMethodsAroundInt ...@@ -40,8 +38,7 @@ public class HandlerMethodInvokerInterceptor implements InstanceMethodsAroundInt
Object handler = allArguments[1]; Object handler = allArguments[1];
if (handler instanceof EnhancedInstance) { if (handler instanceof EnhancedInstance) {
ContextManager.getRuntimeContext() ContextManager.getRuntimeContext()
.put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder( .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, ((NativeWebRequest) allArguments[2]).getNativeResponse());
(HttpServletResponse) ((NativeWebRequest) allArguments[2]).getNativeResponse()));
} }
} }
......
...@@ -22,16 +22,14 @@ import javax.servlet.http.HttpServletRequest; ...@@ -22,16 +22,14 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants; import org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
public final class SpringTestCaseHelper { public final class SpringTestCaseHelper {
public final static void createCaseHandler(HttpServletRequest request, HttpServletResponse response, public final static void createCaseHandler(HttpServletRequest request, HttpServletResponse response,
CaseHandler a) throws Throwable { CaseHandler a) throws Throwable {
ContextManager.createLocalSpan("For-Test"); ContextManager.createLocalSpan("For-Test");
ContextManager.getRuntimeContext().put(Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder(request)); ContextManager.getRuntimeContext().put(Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT, request);
ContextManager.getRuntimeContext().put(Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder(response)); ContextManager.getRuntimeContext().put(Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT, response);
a.handleCase(); a.handleCase();
ContextManager.stopSpan(); ContextManager.stopSpan();
ContextManager.getRuntimeContext().remove(Constants.CONTROLLER_METHOD_STACK_DEPTH); ContextManager.getRuntimeContext().remove(Constants.CONTROLLER_METHOD_STACK_DEPTH);
......
...@@ -22,8 +22,6 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager; ...@@ -22,8 +22,6 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; 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.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
...@@ -50,12 +48,12 @@ public class GetBeanInterceptor implements InstanceMethodsAroundInterceptor { ...@@ -50,12 +48,12 @@ public class GetBeanInterceptor implements InstanceMethodsAroundInterceptor {
ContextManager.getRuntimeContext() ContextManager.getRuntimeContext()
.put( .put(
REQUEST_KEY_IN_RUNTIME_CONTEXT, REQUEST_KEY_IN_RUNTIME_CONTEXT,
new JavaxServletRequestHolder(requestAttributes.getRequest()) requestAttributes.getRequest()
); );
ContextManager.getRuntimeContext() ContextManager.getRuntimeContext()
.put( .put(
RESPONSE_KEY_IN_RUNTIME_CONTEXT, RESPONSE_KEY_IN_RUNTIME_CONTEXT,
new JavaxServletResponseHolder(requestAttributes.getResponse()) requestAttributes.getResponse()
); );
} }
} }
......
...@@ -17,48 +17,56 @@ ...@@ -17,48 +17,56 @@
package org.apache.skywalking.apm.plugin.spring.mvc.v5; package org.apache.skywalking.apm.plugin.spring.mvc.v5;
import java.lang.reflect.Method;
import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.RuntimeContext;
import org.apache.skywalking.apm.agent.core.context.tag.Tags; 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.AbstractSpan;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; 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.v2.InstanceMethodsAroundInterceptorV2;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.ReactiveRequestHolder;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.ReactiveResponseHolder;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.lang.reflect.Method;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
public class InvokeInterceptor implements InstanceMethodsAroundInterceptor { public class InvokeInterceptor implements InstanceMethodsAroundInterceptorV2 {
@Override @Override
public void beforeMethod(final EnhancedInstance objInst, public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInvocationContext context) throws Throwable {
final Method method,
final Object[] allArguments,
final Class<?>[] argumentsTypes,
final MethodInterceptResult result) throws Throwable {
ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; ServerWebExchange exchange = (ServerWebExchange) allArguments[0];
final ReactiveResponseHolder responseHolder = new ReactiveResponseHolder(exchange.getResponse()); final ServerHttpResponse response = exchange.getResponse();
ContextManager.getRuntimeContext() /**
.put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, responseHolder); * First, we put the slot in the RuntimeContext,
ContextManager.getRuntimeContext() * as well as the MethodInvocationContext (MIC),
.put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new ReactiveRequestHolder(exchange.getRequest())); * so that we can access it in the {@link org.apache.skywalking.apm.plugin.spring.mvc.v5.InvokeInterceptor#afterMethod}
objInst.setSkyWalkingDynamicField(responseHolder); * Then we fetch the slot from {@link org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor.AbstractMethodInterceptor#afterMethod}
* and fulfill the slot with the real AbstractSpan.
* Afterwards, we can safely remove the RuntimeContext.
* Finally, when the lambda executes in the {@link reactor.core.publisher.Mono#doFinally},
* ref of span could be acquired from MIC.
*/
AbstractSpan[] ref = new AbstractSpan[1];
RuntimeContext runtimeContext = ContextManager.getRuntimeContext();
runtimeContext.put(REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT, ref);
runtimeContext.put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, response);
runtimeContext.put(REQUEST_KEY_IN_RUNTIME_CONTEXT, exchange.getRequest());
context.setContext(ref);
} }
@Override @Override
public Object afterMethod(final EnhancedInstance objInst, public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret, MethodInvocationContext context) throws Throwable {
final Method method,
final Object[] allArguments,
final Class<?>[] argumentsTypes,
final Object ret) throws Throwable {
ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; ServerWebExchange exchange = (ServerWebExchange) allArguments[0];
return ((Mono) ret).doFinally(s -> { return ((Mono) ret).doFinally(s -> {
ReactiveResponseHolder responseHolder = (ReactiveResponseHolder) objInst.getSkyWalkingDynamicField(); Object ctx = context.getContext();
AbstractSpan span = responseHolder.getSpan(); if (ctx == null) {
return;
}
AbstractSpan span = ((AbstractSpan[]) ctx)[0];
if (span == null) { if (span == null) {
return; return;
} }
...@@ -72,11 +80,7 @@ public class InvokeInterceptor implements InstanceMethodsAroundInterceptor { ...@@ -72,11 +80,7 @@ public class InvokeInterceptor implements InstanceMethodsAroundInterceptor {
} }
@Override @Override
public void handleMethodException(final EnhancedInstance objInst, public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t, MethodInvocationContext context) {
final Method method,
final Object[] allArguments,
final Class<?>[] argumentsTypes,
final Throwable t) {
} }
} }
...@@ -20,10 +20,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define; ...@@ -20,10 +20,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
public abstract class AbstractSpring5Instrumentation extends ClassInstanceMethodsEnhancePluginDefine { public abstract class AbstractSpring5Instrumentation extends ClassInstanceMethodsEnhancePluginDefine {
public static final String WITHNESS_CLASSES = "org.springframework.web.servlet.resource.HttpResource"; public static final String WITNESS_CLASSES = "org.springframework.web.servlet.resource.HttpResource";
@Override @Override
protected final String[] witnessClasses() { protected final String[] witnessClasses() {
return new String[] {WITHNESS_CLASSES}; return new String[] {WITNESS_CLASSES};
} }
} }
...@@ -20,10 +20,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive; ...@@ -20,10 +20,10 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
public abstract class AbstractSpring5ReactiveInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { public abstract class AbstractSpring5ReactiveInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
public static final String WITHNESS_CLASSES = "org.springframework.web.reactive.result.method.InvocableHandlerMethod"; public static final String WITNESS_CLASSES = "org.springframework.web.reactive.result.method.InvocableHandlerMethod";
@Override @Override
protected final String[] witnessClasses() { protected final String[] witnessClasses() {
return new String[] {WITHNESS_CLASSES}; return new String[] {WITNESS_CLASSES};
} }
} }
/*
* 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.spring.mvc.v5.define.reactive;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassInstanceMethodsEnhancePluginDefineV2;
public abstract class AbstractSpring5ReactiveInstrumentationV2 extends ClassInstanceMethodsEnhancePluginDefineV2 {
public static final String WITNESS_CLASSES = "org.springframework.web.reactive.result.method.InvocableHandlerMethod";
@Override
protected final String[] witnessClasses() {
return new String[] {WITNESS_CLASSES};
}
}
...@@ -20,15 +20,14 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive; ...@@ -20,15 +20,14 @@ package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive;
import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; 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.v2.InstanceMethodsInterceptV2Point;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType;
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { public class InvocableHandlerMethodInstrumentation extends AbstractSpring5ReactiveInstrumentationV2 {
@Override @Override
protected ClassMatch enhanceClass() { protected ClassMatch enhanceClass() {
return byName("org.springframework.web.reactive.result.method.InvocableHandlerMethod"); return byName("org.springframework.web.reactive.result.method.InvocableHandlerMethod");
...@@ -40,9 +39,9 @@ public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsE ...@@ -40,9 +39,9 @@ public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsE
} }
@Override @Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() {
return new InstanceMethodsInterceptPoint[] { return new InstanceMethodsInterceptV2Point[] {
new InstanceMethodsInterceptPoint() { new InstanceMethodsInterceptV2Point() {
@Override @Override
public ElementMatcher<MethodDescription> getMethodsMatcher() { public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("invoke").and( return named("invoke").and(
...@@ -50,7 +49,7 @@ public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsE ...@@ -50,7 +49,7 @@ public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsE
} }
@Override @Override
public String getMethodsInterceptor() { public String getMethodsInterceptorV2() {
return "org.apache.skywalking.apm.plugin.spring.mvc.v5.InvokeInterceptor"; return "org.apache.skywalking.apm.plugin.spring.mvc.v5.InvokeInterceptor";
} }
......
...@@ -34,6 +34,8 @@ public class Constants { ...@@ -34,6 +34,8 @@ public class Constants {
public static final String RESPONSE_KEY_IN_RUNTIME_CONTEXT = "SW_RESPONSE"; public static final String RESPONSE_KEY_IN_RUNTIME_CONTEXT = "SW_RESPONSE";
public static final String REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT = "SW_REACTIVE_RESPONSE_ASYNC_SPAN";
public static final String FORWARD_REQUEST_FLAG = "SW_FORWARD_REQUEST_FLAG"; public static final String FORWARD_REQUEST_FLAG = "SW_FORWARD_REQUEST_FLAG";
public static final String WEBFLUX_REQUEST_KEY = "SW_WEBFLUX_REQUEST_KEY"; public static final String WEBFLUX_REQUEST_KEY = "SW_WEBFLUX_REQUEST_KEY";
......
/*
* 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.spring.mvc.commons;
import java.util.Enumeration;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
public class JavaxServletRequestHolder implements RequestHolder {
private final HttpServletRequest request;
public JavaxServletRequestHolder(HttpServletRequest request) {
this.request = request;
}
@Override
public String getHeader(final String headerName) {
return request.getHeader(headerName);
}
@Override
public Enumeration<String> getHeaders(final String headerName) {
return request.getHeaders(headerName);
}
@Override
public String requestURL() {
return request.getRequestURL().toString();
}
@Override
public String requestMethod() {
return request.getMethod();
}
@Override
public Map<String, String[]> getParameterMap() {
return request.getParameterMap();
}
}
/*
* 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.spring.mvc.commons;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.springframework.http.server.reactive.ServerHttpResponse;
public class ReactiveResponseHolder implements ResponseHolder {
private final ServerHttpResponse response;
private AbstractSpan span;
public ReactiveResponseHolder(final ServerHttpResponse response) {
this.response = response;
}
public void setSpan(AbstractSpan span) {
this.span = span;
}
public AbstractSpan getSpan() {
return this.span;
}
@Override
public int statusCode() {
return response.getStatusCode().value();
}
}
/*
* 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.spring.mvc.commons;
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.util.CollectionUtil;
import org.apache.skywalking.apm.util.StringUtil;
import org.springframework.http.server.reactive.ServerHttpRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RequestUtil {
public static void collectHttpParam(HttpServletRequest request, AbstractSpan span) {
final Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap != null && !parameterMap.isEmpty()) {
String tagValue = CollectionUtil.toString(parameterMap);
tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ?
StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.PARAMS.set(span, tagValue);
}
}
public static void collectHttpParam(ServerHttpRequest request, AbstractSpan span) {
Map<String, String[]> parameterMap = new HashMap<>(request.getQueryParams().size());
request.getQueryParams().forEach((key, value) -> {
parameterMap.put(key, value.toArray(new String[0]));
});
if (!parameterMap.isEmpty()) {
String tagValue = CollectionUtil.toString(parameterMap);
tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ?
StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.PARAMS.set(span, tagValue);
}
}
public static void collectHttpHeaders(HttpServletRequest request, AbstractSpan span) {
final List<String> headersList = new ArrayList<>(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size());
SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream()
.filter(
headerName -> request.getHeaders(headerName) != null)
.forEach(headerName -> {
Enumeration<String> headerValues = request.getHeaders(
headerName);
List<String> valueList = Collections.list(
headerValues);
if (!CollectionUtil.isEmpty(valueList)) {
String headerValue = valueList.toString();
headersList.add(headerName + "=" + headerValue);
}
});
collectHttpHeaders(headersList, span);
}
public static void collectHttpHeaders(ServerHttpRequest request, AbstractSpan span) {
final List<String> headersList = new ArrayList<>(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size());
SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream()
.filter(headerName -> getHeaders(request, headerName).hasMoreElements())
.forEach(headerName -> {
Enumeration<String> headerValues = getHeaders(request, headerName);
List<String> valueList = Collections.list(
headerValues);
if (!CollectionUtil.isEmpty(valueList)) {
String headerValue = valueList.toString();
headersList.add(headerName + "=" + headerValue);
}
});
collectHttpHeaders(headersList, span);
}
private static void collectHttpHeaders(final List<String> headersList, final AbstractSpan span) {
if (headersList != null && !headersList.isEmpty()) {
String tagValue = String.join("\n", headersList);
tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD > 0 ?
StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.HEADERS.set(span, tagValue);
}
}
public static Enumeration<String> getHeaders(final ServerHttpRequest request, final String headerName) {
List<String> values = request.getHeaders().get(headerName);
if (values == null) {
return Collections.enumeration(Collections.emptyList());
}
return Collections.enumeration(values);
}
}
/*
* 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.spring.mvc.commons;
public interface ResponseHolder {
int statusCode();
}
...@@ -18,16 +18,10 @@ ...@@ -18,16 +18,10 @@
package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor; package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.skywalking.apm.agent.core.context.CarrierItem; import org.apache.skywalking.apm.agent.core.context.CarrierItem;
import org.apache.skywalking.apm.agent.core.context.ContextCarrier; import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.RuntimeContext;
import org.apache.skywalking.apm.agent.core.context.tag.Tags; 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.AbstractSpan;
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
...@@ -38,16 +32,20 @@ import org.apache.skywalking.apm.agent.core.util.CollectionUtil; ...@@ -38,16 +32,20 @@ import org.apache.skywalking.apm.agent.core.util.CollectionUtil;
import org.apache.skywalking.apm.agent.core.util.MethodUtil; import org.apache.skywalking.apm.agent.core.util.MethodUtil;
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.EnhanceRequireObjectCache; import org.apache.skywalking.apm.plugin.spring.mvc.commons.EnhanceRequireObjectCache;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder; import org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestUtil;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.ResponseHolder;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.ReactiveResponseHolder;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.SpringMVCPluginConfig; import org.apache.skywalking.apm.plugin.spring.mvc.commons.SpringMVCPluginConfig;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.IllegalMethodStackDepthException; import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.IllegalMethodStackDepthException;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.ServletResponseNotFoundException; import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.ServletResponseNotFoundException;
import org.apache.skywalking.apm.util.StringUtil; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.CONTROLLER_METHOD_STACK_DEPTH; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.CONTROLLER_METHOD_STACK_DEPTH;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.FORWARD_REQUEST_FLAG; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.FORWARD_REQUEST_FLAG;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
...@@ -60,9 +58,17 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround ...@@ -60,9 +58,17 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
private static final String SERVLET_RESPONSE_CLASS = "javax.servlet.http.HttpServletResponse"; private static final String SERVLET_RESPONSE_CLASS = "javax.servlet.http.HttpServletResponse";
private static final String GET_STATUS_METHOD = "getStatus"; private static final String GET_STATUS_METHOD = "getStatus";
private static boolean IN_SERVLET_CONTAINER;
static { static {
IS_SERVLET_GET_STATUS_METHOD_EXIST = MethodUtil.isMethodExist( IS_SERVLET_GET_STATUS_METHOD_EXIST = MethodUtil.isMethodExist(
AbstractMethodInterceptor.class.getClassLoader(), SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD); AbstractMethodInterceptor.class.getClassLoader(), SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD);
try {
Class.forName(SERVLET_RESPONSE_CLASS, true, AbstractMethodInterceptor.class.getClassLoader());
IN_SERVLET_CONTAINER = true;
} catch (Exception ignore) {
IN_SERVLET_CONTAINER = false;
}
} }
public abstract String getRequestURL(Method method); public abstract String getRequestURL(Method method);
...@@ -96,31 +102,58 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround ...@@ -96,31 +102,58 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
operationName = getAcceptedMethodTypes(method) + requestURL; operationName = getAcceptedMethodTypes(method) + requestURL;
} }
RequestHolder request = (RequestHolder) ContextManager.getRuntimeContext() Object request = ContextManager.getRuntimeContext().get(REQUEST_KEY_IN_RUNTIME_CONTEXT);
.get(REQUEST_KEY_IN_RUNTIME_CONTEXT);
if (request != null) { if (request != null) {
StackDepth stackDepth = (StackDepth) ContextManager.getRuntimeContext().get(CONTROLLER_METHOD_STACK_DEPTH); StackDepth stackDepth = (StackDepth) ContextManager.getRuntimeContext().get(CONTROLLER_METHOD_STACK_DEPTH);
if (stackDepth == null) { if (stackDepth == null) {
ContextCarrier contextCarrier = new ContextCarrier(); final ContextCarrier contextCarrier = new ContextCarrier();
CarrierItem next = contextCarrier.items();
while (next.hasNext()) { if (IN_SERVLET_CONTAINER && HttpServletRequest.class.isAssignableFrom(request.getClass())) {
next = next.next(); final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
next.setHeadValue(request.getHeader(next.getHeadKey())); CarrierItem next = contextCarrier.items();
} while (next.hasNext()) {
next = next.next();
AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); next.setHeadValue(httpServletRequest.getHeader(next.getHeadKey()));
Tags.URL.set(span, request.requestURL()); }
Tags.HTTP.METHOD.set(span, request.requestMethod());
span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION); AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier);
SpanLayer.asHttp(span); Tags.URL.set(span, httpServletRequest.getRequestURL().toString());
Tags.HTTP.METHOD.set(span, httpServletRequest.getMethod());
if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) { span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION);
collectHttpParam(request, span); SpanLayer.asHttp(span);
}
if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) {
if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) { RequestUtil.collectHttpParam(httpServletRequest, span);
collectHttpHeaders(request, span); }
if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) {
RequestUtil.collectHttpHeaders(httpServletRequest, span);
}
} else if (ServerHttpRequest.class.isAssignableFrom(request.getClass())) {
final ServerHttpRequest serverHttpRequest = (ServerHttpRequest) request;
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
next = next.next();
next.setHeadValue(serverHttpRequest.getHeaders().getFirst(next.getHeadKey()));
}
AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier);
Tags.URL.set(span, serverHttpRequest.getURI().toString());
Tags.HTTP.METHOD.set(span, serverHttpRequest.getMethodValue());
span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION);
SpanLayer.asHttp(span);
if (SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS) {
RequestUtil.collectHttpParam(serverHttpRequest, span);
}
if (!CollectionUtil.isEmpty(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) {
RequestUtil.collectHttpHeaders(serverHttpRequest, span);
}
} else {
throw new IllegalStateException("this line should not be reached");
} }
stackDepth = new StackDepth(); stackDepth = new StackDepth();
...@@ -136,8 +169,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround ...@@ -136,8 +169,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
private String buildOperationName(Object invoker, Method method) { private String buildOperationName(Object invoker, Method method) {
StringBuilder operationName = new StringBuilder(invoker.getClass().getName()).append(".") StringBuilder operationName = new StringBuilder(invoker.getClass().getName()).append(".")
.append(method.getName()) .append(method.getName())
.append("("); .append("(");
for (Class<?> type : method.getParameterTypes()) { for (Class<?> type : method.getParameterTypes()) {
operationName.append(type.getName()).append(","); operationName.append(type.getName()).append(",");
} }
...@@ -152,7 +185,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround ...@@ -152,7 +185,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
@Override @Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable { Object ret) throws Throwable {
Boolean forwardRequestFlag = (Boolean) ContextManager.getRuntimeContext().get(FORWARD_REQUEST_FLAG); final RuntimeContext runtimeContext = ContextManager.getRuntimeContext();
Boolean forwardRequestFlag = (Boolean) runtimeContext.get(FORWARD_REQUEST_FLAG);
/** /**
* Spring MVC plugin do nothing if current request is forward request. * Spring MVC plugin do nothing if current request is forward request.
* Ref: https://github.com/apache/skywalking/pull/1325 * Ref: https://github.com/apache/skywalking/pull/1325
...@@ -161,11 +195,10 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround ...@@ -161,11 +195,10 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
return ret; return ret;
} }
RequestHolder request = (RequestHolder) ContextManager.getRuntimeContext() Object request = runtimeContext.get(REQUEST_KEY_IN_RUNTIME_CONTEXT);
.get(REQUEST_KEY_IN_RUNTIME_CONTEXT);
if (request != null) { if (request != null) {
StackDepth stackDepth = (StackDepth) ContextManager.getRuntimeContext().get(CONTROLLER_METHOD_STACK_DEPTH); StackDepth stackDepth = (StackDepth) runtimeContext.get(CONTROLLER_METHOD_STACK_DEPTH);
if (stackDepth == null) { if (stackDepth == null) {
throw new IllegalMethodStackDepthException(); throw new IllegalMethodStackDepthException();
} else { } else {
...@@ -175,30 +208,43 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround ...@@ -175,30 +208,43 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
AbstractSpan span = ContextManager.activeSpan(); AbstractSpan span = ContextManager.activeSpan();
if (stackDepth.depth() == 0) { if (stackDepth.depth() == 0) {
ResponseHolder response = (ResponseHolder) ContextManager.getRuntimeContext() Object response = runtimeContext.get(RESPONSE_KEY_IN_RUNTIME_CONTEXT);
.get(
RESPONSE_KEY_IN_RUNTIME_CONTEXT);
if (response == null) { if (response == null) {
throw new ServletResponseNotFoundException(); throw new ServletResponseNotFoundException();
} }
if (IS_SERVLET_GET_STATUS_METHOD_EXIST && response.statusCode() >= 400) { Integer statusCode = null;
span.errorOccurred();
Tags.STATUS_CODE.set(span, Integer.toString(response.statusCode())); if (IS_SERVLET_GET_STATUS_METHOD_EXIST && HttpServletResponse.class.isAssignableFrom(response.getClass())) {
statusCode = ((HttpServletResponse) response).getStatus();
} else if (ServerHttpResponse.class.isAssignableFrom(response.getClass())) {
if (IS_SERVLET_GET_STATUS_METHOD_EXIST) {
statusCode = ((ServerHttpResponse) response).getRawStatusCode();
}
Object context = runtimeContext.get(REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT);
if (context != null) {
((AbstractSpan[]) context)[0] = span.prepareForAsync();
}
} }
if (response instanceof ReactiveResponseHolder) {
ReactiveResponseHolder reactiveResponse = (ReactiveResponseHolder) response; if (statusCode != null && statusCode >= 400) {
AbstractSpan async = span.prepareForAsync(); span.errorOccurred();
reactiveResponse.setSpan(async); Tags.STATUS_CODE.set(span, Integer.toString(statusCode));
} }
ContextManager.getRuntimeContext().remove(REQUEST_KEY_IN_RUNTIME_CONTEXT);
ContextManager.getRuntimeContext().remove(RESPONSE_KEY_IN_RUNTIME_CONTEXT); runtimeContext.remove(REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT);
ContextManager.getRuntimeContext().remove(CONTROLLER_METHOD_STACK_DEPTH); runtimeContext.remove(REQUEST_KEY_IN_RUNTIME_CONTEXT);
runtimeContext.remove(RESPONSE_KEY_IN_RUNTIME_CONTEXT);
runtimeContext.remove(CONTROLLER_METHOD_STACK_DEPTH);
} }
// Active HTTP parameter collection automatically in the profiling context. // Active HTTP parameter collection automatically in the profiling context.
if (!SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS && span.isProfiling()) { if (!SpringMVCPluginConfig.Plugin.SpringMVC.COLLECT_HTTP_PARAMS && span.isProfiling()) {
collectHttpParam(request, span); if (HttpServletRequest.class.isAssignableFrom(request.getClass())) {
RequestUtil.collectHttpParam((HttpServletRequest) request, span);
} else if (ServerHttpRequest.class.isAssignableFrom(request.getClass())) {
RequestUtil.collectHttpParam((ServerHttpRequest) request, span);
}
} }
ContextManager.stopSpan(); ContextManager.stopSpan();
...@@ -212,38 +258,4 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround ...@@ -212,38 +258,4 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
Class<?>[] argumentsTypes, Throwable t) { Class<?>[] argumentsTypes, Throwable t) {
ContextManager.activeSpan().log(t); ContextManager.activeSpan().log(t);
} }
private void collectHttpParam(RequestHolder request, AbstractSpan span) {
final Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap != null && !parameterMap.isEmpty()) {
String tagValue = CollectionUtil.toString(parameterMap);
tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ?
StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.PARAMS.set(span, tagValue);
}
}
private void collectHttpHeaders(RequestHolder request, AbstractSpan span) {
final List<String> headersList = new ArrayList<>(SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size());
SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream()
.filter(
headerName -> request.getHeaders(headerName) != null)
.forEach(headerName -> {
Enumeration<String> headerValues = request.getHeaders(
headerName);
List<String> valueList = Collections.list(
headerValues);
if (!CollectionUtil.isEmpty(valueList)) {
String headerValue = valueList.toString();
headersList.add(headerName + "=" + headerValue);
}
});
if (!headersList.isEmpty()) {
String tagValue = headersList.stream().collect(Collectors.joining("\n"));
tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD > 0 ?
StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD) : tagValue;
Tags.HTTP.HEADERS.set(span, tagValue);
}
}
} }
...@@ -23,7 +23,6 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager; ...@@ -23,7 +23,6 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; 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.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
...@@ -45,8 +44,8 @@ public class GetBeanInterceptor implements InstanceMethodsAroundInterceptor { ...@@ -45,8 +44,8 @@ public class GetBeanInterceptor implements InstanceMethodsAroundInterceptor {
Object ret) throws Throwable { Object ret) throws Throwable {
if (ret instanceof EnhancedInstance) { if (ret instanceof EnhancedInstance) {
ContextManager.getRuntimeContext() ContextManager.getRuntimeContext()
.put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) .put(REQUEST_KEY_IN_RUNTIME_CONTEXT, ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest())); .getRequest());
} }
return ret; return ret;
} }
......
...@@ -19,12 +19,10 @@ ...@@ -19,12 +19,10 @@
package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor; package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.servlet.http.HttpServletResponse;
import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; 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.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
...@@ -38,8 +36,7 @@ public class InvokeForRequestInterceptor implements InstanceMethodsAroundInterce ...@@ -38,8 +36,7 @@ public class InvokeForRequestInterceptor implements InstanceMethodsAroundInterce
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable { MethodInterceptResult result) throws Throwable {
ContextManager.getRuntimeContext() ContextManager.getRuntimeContext()
.put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder( .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, ((NativeWebRequest) allArguments[0]).getNativeResponse());
(HttpServletResponse) ((NativeWebRequest) allArguments[0]).getNativeResponse()));
} }
@Override @Override
......
...@@ -18,15 +18,12 @@ ...@@ -18,15 +18,12 @@
package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor; package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; 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.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder;
import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder; import java.lang.reflect.Method;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT;
import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT; import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
...@@ -36,10 +33,8 @@ public class InvokeHandlerMethodInterceptor implements InstanceMethodsAroundInte ...@@ -36,10 +33,8 @@ public class InvokeHandlerMethodInterceptor implements InstanceMethodsAroundInte
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable { MethodInterceptResult result) throws Throwable {
if (allArguments[2] instanceof EnhancedInstance) { if (allArguments[2] instanceof EnhancedInstance) {
ContextManager.getRuntimeContext().put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder( ContextManager.getRuntimeContext().put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, allArguments[1]);
(HttpServletResponse) allArguments[1])); ContextManager.getRuntimeContext().put(REQUEST_KEY_IN_RUNTIME_CONTEXT, allArguments[0]);
ContextManager.getRuntimeContext().put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder(
(HttpServletRequest) allArguments[0]));
} }
} }
......
#!/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.
set -ex
home="$(cd "$(dirname $0)"; pwd)"
classpath="$(printf %s: $home/../target/3rd-lib/*.jar)${home}/../target/classes"
java ${agent_opts} -classpath "$classpath" test.apache.skywalking.apm.testcase.sc.springmvcreactive.Application &
# 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: springmvc-reactive-devtools-scenario
segmentSize: ge 2
segments:
- segmentId: not null
spans:
- operationName: H2/JDBI/PreparedStatement/executeQuery
operationId: 0
parentSpanId: 0
spanId: 1
spanLayer: Database
startTime: not null
endTime: not null
componentId: 32
isError: false
spanType: Exit
peer: not null
tags:
- {key: db.type, value: not null}
- {key: db.instance, value: test}
- {key: db.statement, value: not null}
skipAnalysis: 'false'
- operationName: '/testcase/error'
operationId: 0
parentSpanId: 0
spanId: 2
spanLayer: Http
startTime: not null
endTime: not null
isError: false
componentId: not null
tags:
- {key: url, value: not null}
- {key: http.method, value: GET}
skipAnalysis: 'false'
- operationName: 'future/get:/testcase/error'
operationId: 0
parentSpanId: 0
spanId: 3
spanLayer: not null
startTime: not null
endTime: not null
isError: true
componentId: not null
logs:
- logEvent:
- {key: event, value: error}
- {key: error.kind, value: not null}
- {key: message, value: not null}
- {key: stack, value: not null}
skipAnalysis: 'false'
- operationName: '{GET}/testcase/{test}'
operationId: 0
parentSpanId: -1
spanId: 0
spanLayer: Http
startTime: not null
endTime: not null
componentId: 14
isError: false
tags:
- {key: url, value: not null}
- {key: http.method, value: GET}
skipAnalysis: 'false'
- segmentId: not null
spans:
- operationName: '{GET}/testcase/error'
operationId: 0
parentSpanId: -1
spanId: 0
spanLayer: Http
startTime: not null
endTime: not null
componentId: 14
spanType: Entry
isError: true
tags:
- {key: url, value: not null}
- {key: http.method, value: GET}
- {key: status_code, value: '500'}
logs:
- logEvent:
- {key: event, value: error}
- {key: error.kind, value: not null}
- {key: message, value: not null}
- {key: stack, value: not null}
skipAnalysis: 'false'
\ No newline at end of file
# 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/testcase/test
healthCheck: http://localhost:8080/testcase/healthCheck
startScript: ./bin/startup.sh
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.skywalking</groupId>
<artifactId>springmvc-reactive-devtools-scenario</artifactId>
<packaging>jar</packaging>
<version>5.0.0</version>
<name>skywalking-springmvc-reactive-devtools-scenario</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<test.framework.version>2.1.1.RELEASE</test.framework.version>
<docker.image.version>${test.framework.version}</docker.image.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>${test.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<version>${test.framework.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
</dependencies>
<build>
<finalName>springmvc-reactive-devtools-scenario</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<type>jar</type>
<includeTypes>jar</includeTypes>
<includeScope>runtime</includeScope>
<outputDirectory>
${project.build.directory}/3rd-lib/
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${test.framework.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
<outputDirectory>./target/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
~
-->
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>./bin</directory>
<fileMode>0775</fileMode>
</fileSet>
<fileSet>
<directory>${project.build.directory}/classes</directory>
<fileMode>0775</fileMode>
</fileSet>
<fileSet>
<directory>${project.build.directory}/3rd-lib</directory>
<fileMode>0775</fileMode>
</fileSet>
</fileSets>
<files>
<file>
<source>${project.build.directory}/springmvc-reactive-devtools-scenario.jar</source>
<outputDirectory>./libs</outputDirectory>
<fileMode>0775</fileMode>
</file>
</files>
</assembly>
...@@ -15,19 +15,15 @@ ...@@ -15,19 +15,15 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.skywalking.apm.plugin.spring.mvc.commons; package test.apache.skywalking.apm.testcase.sc.springmvcreactive;
import java.util.Enumeration; import org.springframework.boot.SpringApplication;
import java.util.Map; import org.springframework.boot.autoconfigure.SpringBootApplication;
public interface RequestHolder { @SpringBootApplication
String getHeader(String headerName); public class Application {
Enumeration<String> getHeaders(String headerName); public static void main(String[] args) {
SpringApplication.run(Application.class, args);
String requestURL(); }
String requestMethod();
Map<String, String[]> getParameterMap();
} }
...@@ -15,52 +15,46 @@ ...@@ -15,52 +15,46 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.skywalking.apm.plugin.spring.mvc.commons; package test.apache.skywalking.apm.testcase.sc.springmvcreactive.controller;
import java.util.Collections; import java.sql.SQLException;
import java.util.Enumeration; import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashMap; import org.springframework.http.ResponseEntity;
import java.util.List; import org.springframework.util.concurrent.ListenableFuture;
import java.util.Map; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
public class ReactiveRequestHolder implements RequestHolder { import org.springframework.web.bind.annotation.RequestMapping;
private final ServerHttpRequest serverHttpRequest; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.AsyncRestTemplate;
public ReactiveRequestHolder(final ServerHttpRequest serverHttpRequest) { import reactor.core.publisher.Mono;
this.serverHttpRequest = serverHttpRequest; import test.apache.skywalking.apm.testcase.sc.springmvcreactive.service.TestService;
}
@RestController
@Override public class Controller {
public String getHeader(final String headerName) {
return this.serverHttpRequest.getHeaders().getFirst(headerName); @Autowired
private TestService testService;
@RequestMapping("/testcase/healthCheck")
public String healthCheck() {
return "healthCheck";
} }
@Override @GetMapping("/testcase/{test}")
public Enumeration<String> getHeaders(final String headerName) { public Mono<String> hello(@RequestBody(required = false) String body, @PathVariable("test") String test) throws SQLException {
List<String> values = this.serverHttpRequest.getHeaders().get(headerName); testService.executeSQL();
if (values == null) { ListenableFuture<ResponseEntity<String>> forEntity = new AsyncRestTemplate().getForEntity("http://localhost:8080/testcase/error", String.class);
return Collections.enumeration(Collections.EMPTY_LIST); try {
forEntity.get();
} catch (Exception e) {
} }
return Collections.enumeration(values); return Mono.just("Hello World");
}
@Override
public String requestURL() {
return this.serverHttpRequest.getURI().toString();
} }
@Override @GetMapping("/testcase/error")
public String requestMethod() { public Mono<String> error() {
return this.serverHttpRequest.getMethodValue(); throw new RuntimeException("this is Error");
} }
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> parameterMap = new HashMap<>(this.serverHttpRequest.getQueryParams().size());
this.serverHttpRequest.getQueryParams().forEach((key, value) -> {
parameterMap.put(key, value.toArray(new String[0]));
});
return parameterMap;
}
} }
...@@ -15,21 +15,28 @@ ...@@ -15,21 +15,28 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.skywalking.apm.plugin.spring.mvc.commons; package test.apache.skywalking.apm.testcase.sc.springmvcreactive.service;
import javax.servlet.http.HttpServletResponse; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Service;
public class JavaxServletResponseHolder implements ResponseHolder { @Service
public class TestService {
private final HttpServletResponse response; private Connection connection;
public JavaxServletResponseHolder(final HttpServletResponse response) { @PostConstruct
this.response = response; private void setUp() throws SQLException {
connection = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "sa", "");
} }
@Override public void executeSQL() throws SQLException {
public int statusCode() { PreparedStatement preparedStatement = connection.prepareStatement("SELECT 1 = 1");
return response.getStatus(); preparedStatement.executeQuery();
} }
} }
#
# 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
\ No newline at end of file
# 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.
# 2.0.0-2.1.0 are supported, but due to the status code bug(https://github.com/spring-projects/spring-framework/issues/21901)
# we don’t test them
2.1.7.RELEASE
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.9.RELEASE</version> <version>${test.framework.version}</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册