Add new integration for Servlet Filters, HttpServlet, and RequestDispatcher
Disabled by default, and only creates a span if existing trace detected. To enable all of them: * System Property: `-Ddd.integration.servlet.enabled=true` * Environment Variable: `DD_INTEGRATION_SERVLET_ENABLED=true` (They have independent configs as well. If needed, view the source below.)
This commit is contained in:
parent
df387b486a
commit
425156115c
|
@ -5,7 +5,7 @@ import java.io.Closeable;
|
|||
public interface AgentScope extends Closeable {
|
||||
AgentSpan span();
|
||||
|
||||
void setAsyncPropagation(boolean value);
|
||||
AgentScope setAsyncPropagation(boolean value);
|
||||
|
||||
@Override
|
||||
void close();
|
||||
|
|
|
@ -208,7 +208,9 @@ public class AgentTracer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setAsyncPropagation(final boolean value) {}
|
||||
public AgentScope setAsyncPropagation(final boolean value) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {}
|
||||
|
|
|
@ -237,10 +237,11 @@ public final class OpenTracing32 implements TracerAPI {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setAsyncPropagation(final boolean value) {
|
||||
public AgentScope setAsyncPropagation(final boolean value) {
|
||||
if (scope instanceof TraceScope) {
|
||||
((TraceScope) scope).setAsyncPropagation(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package datadog.trace.instrumentation.servlet;
|
||||
|
||||
import datadog.trace.instrumentation.api.AgentPropagation;
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
/** Inject into request attributes since the request headers can't be modified. */
|
||||
public class ServletRequestSetter implements AgentPropagation.Setter<ServletRequest> {
|
||||
public static final ServletRequestSetter SETTER = new ServletRequestSetter();
|
||||
|
||||
@Override
|
||||
public void set(final ServletRequest carrier, final String key, final String value) {
|
||||
carrier.setAttribute(key, value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package datadog.trace.instrumentation.servlet.dispatcher;
|
||||
|
||||
import datadog.trace.agent.decorator.BaseDecorator;
|
||||
|
||||
public class RequestDispatcherDecorator extends BaseDecorator {
|
||||
public static final RequestDispatcherDecorator DECORATE = new RequestDispatcherDecorator();
|
||||
|
||||
@Override
|
||||
protected String[] instrumentationNames() {
|
||||
return new String[] {"servlet", "servlet-dispatcher"};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String spanType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String component() {
|
||||
return "java-web-servlet-dispatcher";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package datadog.trace.instrumentation.servlet.dispatcher;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activateSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activeSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.propagate;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.startSpan;
|
||||
import static datadog.trace.instrumentation.servlet.ServletRequestSetter.SETTER;
|
||||
import static datadog.trace.instrumentation.servlet.dispatcher.RequestDispatcherDecorator.DECORATE;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.api.DDTags;
|
||||
import datadog.trace.bootstrap.InstrumentationContext;
|
||||
import datadog.trace.instrumentation.api.AgentScope;
|
||||
import datadog.trace.instrumentation.api.AgentSpan;
|
||||
import java.util.Map;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletRequest;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class RequestDispatcherInstrumentation extends Instrumenter.Default {
|
||||
public RequestDispatcherInstrumentation() {
|
||||
super("servlet", "servlet-dispatcher");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
"datadog.trace.instrumentation.servlet.ServletRequestSetter",
|
||||
"datadog.trace.agent.decorator.BaseDecorator",
|
||||
packageName + ".RequestDispatcherDecorator",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface()).and(safeHasSuperType(named("javax.servlet.RequestDispatcher")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> contextStore() {
|
||||
return singletonMap("javax.servlet.RequestDispatcher", String.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("forward")
|
||||
.or(named("include"))
|
||||
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
|
||||
.and(takesArgument(1, named("javax.servlet.ServletResponse")))
|
||||
.and(isPublic()),
|
||||
RequestDispatcherAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class RequestDispatcherAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static AgentScope start(
|
||||
@Advice.Origin("#m") final String method,
|
||||
@Advice.This final RequestDispatcher dispatcher,
|
||||
@Advice.Argument(0) final ServletRequest request) {
|
||||
if (activeSpan() == null) {
|
||||
// Don't want to generate a new top-level span
|
||||
return null;
|
||||
}
|
||||
|
||||
final AgentSpan span = startSpan("servlet." + method);
|
||||
DECORATE.afterStart(span);
|
||||
|
||||
final String target =
|
||||
InstrumentationContext.get(RequestDispatcher.class, String.class).get(dispatcher);
|
||||
span.setTag(DDTags.RESOURCE_NAME, target);
|
||||
|
||||
// In case we lose context, inject trace into to the request.
|
||||
propagate().inject(span, request, SETTER);
|
||||
|
||||
return activateSpan(span, true).setAsyncPropagation(true);
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stop(
|
||||
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
DECORATE.onError(scope, throwable);
|
||||
DECORATE.beforeFinish(scope);
|
||||
scope.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package datadog.trace.instrumentation.servlet.dispatcher;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.bootstrap.InstrumentationContext;
|
||||
import java.util.Map;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class ServletContextInstrumentation extends Instrumenter.Default {
|
||||
public ServletContextInstrumentation() {
|
||||
super("servlet", "servlet-dispatcher");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface()).and(safeHasSuperType(named("javax.servlet.ServletContext")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> contextStore() {
|
||||
return singletonMap("javax.servlet.RequestDispatcher", String.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
returns(named("javax.servlet.RequestDispatcher"))
|
||||
.and(takesArgument(0, String.class))
|
||||
// javax.servlet.ServletContext.getRequestDispatcher
|
||||
// javax.servlet.ServletContext.getNamedDispatcher
|
||||
.and(isPublic()),
|
||||
RequestDispatcherTargetAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class RequestDispatcherTargetAdvice {
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void saveTarget(
|
||||
@Advice.Argument(0) final String target,
|
||||
@Advice.Return final RequestDispatcher dispatcher) {
|
||||
InstrumentationContext.get(RequestDispatcher.class, String.class).put(dispatcher, target);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package datadog.trace.instrumentation.servlet.filter;
|
||||
|
||||
import datadog.trace.agent.decorator.BaseDecorator;
|
||||
|
||||
public class FilterDecorator extends BaseDecorator {
|
||||
public static final FilterDecorator DECORATE = new FilterDecorator();
|
||||
|
||||
@Override
|
||||
protected String[] instrumentationNames() {
|
||||
return new String[] {"servlet", "servlet-filter"};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String spanType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String component() {
|
||||
return "java-web-servlet-filter";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package datadog.trace.instrumentation.servlet.filter;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activateSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activeSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.startSpan;
|
||||
import static datadog.trace.instrumentation.servlet.filter.FilterDecorator.DECORATE;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.api.DDTags;
|
||||
import datadog.trace.instrumentation.api.AgentScope;
|
||||
import datadog.trace.instrumentation.api.AgentSpan;
|
||||
import java.util.Map;
|
||||
import javax.servlet.Filter;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class FilterInstrumentation extends Instrumenter.Default {
|
||||
public FilterInstrumentation() {
|
||||
super("servlet", "servlet-filter");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
"datadog.trace.agent.decorator.BaseDecorator", packageName + ".FilterDecorator",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface()).and(safeHasSuperType(named("javax.servlet.Filter")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("doFilter")
|
||||
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
|
||||
.and(takesArgument(1, named("javax.servlet.ServletResponse")))
|
||||
.and(isPublic()),
|
||||
FilterAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class FilterAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static AgentScope start(@Advice.This final Filter filter) {
|
||||
if (activeSpan() == null) {
|
||||
// Don't want to generate a new top-level span
|
||||
return null;
|
||||
}
|
||||
|
||||
final AgentSpan span = startSpan("servlet.filter");
|
||||
DECORATE.afterStart(span);
|
||||
|
||||
// Here we use "this" instead of "the method target" to distinguish abstract filter instances.
|
||||
span.setTag(DDTags.RESOURCE_NAME, filter.getClass().getSimpleName() + ".doFilter");
|
||||
|
||||
return activateSpan(span, true).setAsyncPropagation(true);
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
DECORATE.onError(scope, throwable);
|
||||
DECORATE.beforeFinish(scope);
|
||||
scope.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package datadog.trace.instrumentation.servlet.http;
|
||||
|
||||
import datadog.trace.agent.decorator.BaseDecorator;
|
||||
|
||||
public class HttpServletDecorator extends BaseDecorator {
|
||||
public static final HttpServletDecorator DECORATE = new HttpServletDecorator();
|
||||
|
||||
@Override
|
||||
protected String[] instrumentationNames() {
|
||||
return new String[] {"servlet", "servlet-service"};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String spanType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String component() {
|
||||
return "java-web-servlet-service";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package datadog.trace.instrumentation.servlet.http;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activateSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activeSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.startSpan;
|
||||
import static datadog.trace.instrumentation.servlet.http.HttpServletDecorator.DECORATE;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.api.DDTags;
|
||||
import datadog.trace.bootstrap.InstrumentationContext;
|
||||
import datadog.trace.instrumentation.api.AgentScope;
|
||||
import datadog.trace.instrumentation.api.AgentSpan;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class HttpServletInstrumentation extends Instrumenter.Default {
|
||||
public HttpServletInstrumentation() {
|
||||
super("servlet", "servlet-service");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
"datadog.trace.agent.decorator.BaseDecorator", packageName + ".HttpServletDecorator",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface()).and(safeHasSuperType(named("javax.servlet.http.HttpServlet")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Here we are instrumenting the protected method for HttpServlet. This should ensure that this
|
||||
* advice is always called after Servlet3Instrumentation which is instrumenting the public method.
|
||||
*/
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("service")
|
||||
.or(nameStartsWith("do")) // doGet, doPost, etc
|
||||
.and(takesArgument(0, named("javax.servlet.http.HttpServletRequest")))
|
||||
.and(takesArgument(1, named("javax.servlet.http.HttpServletResponse")))
|
||||
.and(isProtected().or(isPublic())),
|
||||
HttpServletAdvice.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> contextStore() {
|
||||
return singletonMap(
|
||||
"javax.servlet.http.HttpServletResponse", "javax.servlet.http.HttpServletRequest");
|
||||
}
|
||||
|
||||
public static class HttpServletAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static AgentScope start(
|
||||
@Advice.Origin final Method method,
|
||||
@Advice.Argument(0) final HttpServletRequest request,
|
||||
@Advice.Argument(1) final HttpServletResponse response) {
|
||||
// For use by HttpServletResponseInstrumentation:
|
||||
InstrumentationContext.get(HttpServletResponse.class, HttpServletRequest.class)
|
||||
.put(response, request);
|
||||
|
||||
if (activeSpan() == null) {
|
||||
// Don't want to generate a new top-level span
|
||||
return null;
|
||||
}
|
||||
|
||||
final AgentSpan span = startSpan("servlet." + method.getName());
|
||||
DECORATE.afterStart(span);
|
||||
|
||||
// Here we use the Method instead of "this.class.name" to distinguish calls to "super".
|
||||
span.setTag(DDTags.RESOURCE_NAME, DECORATE.spanNameForMethod(method));
|
||||
|
||||
return activateSpan(span, true).setAsyncPropagation(true);
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
DECORATE.onError(scope, throwable);
|
||||
DECORATE.beforeFinish(scope);
|
||||
scope.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package datadog.trace.instrumentation.servlet.http;
|
||||
|
||||
import datadog.trace.agent.decorator.BaseDecorator;
|
||||
|
||||
public class HttpServletResponseDecorator extends BaseDecorator {
|
||||
public static final HttpServletResponseDecorator DECORATE = new HttpServletResponseDecorator();
|
||||
|
||||
@Override
|
||||
protected String[] instrumentationNames() {
|
||||
return new String[] {"servlet", "servlet-response"};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String spanType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String component() {
|
||||
return "java-web-servlet-response";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package datadog.trace.instrumentation.servlet.http;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activateSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.activeSpan;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.propagate;
|
||||
import static datadog.trace.instrumentation.api.AgentTracer.startSpan;
|
||||
import static datadog.trace.instrumentation.servlet.ServletRequestSetter.SETTER;
|
||||
import static datadog.trace.instrumentation.servlet.http.HttpServletResponseDecorator.DECORATE;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.api.DDTags;
|
||||
import datadog.trace.bootstrap.InstrumentationContext;
|
||||
import datadog.trace.instrumentation.api.AgentScope;
|
||||
import datadog.trace.instrumentation.api.AgentSpan;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class HttpServletResponseInstrumentation extends Instrumenter.Default {
|
||||
public HttpServletResponseInstrumentation() {
|
||||
super("servlet", "servlet-response");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean defaultEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
"datadog.trace.instrumentation.servlet.ServletRequestSetter",
|
||||
"datadog.trace.agent.decorator.BaseDecorator",
|
||||
packageName + ".HttpServletResponseDecorator",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return not(isInterface())
|
||||
.and(safeHasSuperType(named("javax.servlet.http.HttpServletResponse")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(named("sendError").or(named("sendRedirect")), SendAdvice.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> contextStore() {
|
||||
return singletonMap(
|
||||
"javax.servlet.http.HttpServletResponse", "javax.servlet.http.HttpServletRequest");
|
||||
}
|
||||
|
||||
public static class SendAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static AgentScope start(
|
||||
@Advice.Origin("#m") final String method, @Advice.This final HttpServletResponse resp) {
|
||||
if (activeSpan() == null) {
|
||||
// Don't want to generate a new top-level span
|
||||
return null;
|
||||
}
|
||||
|
||||
final HttpServletRequest req =
|
||||
InstrumentationContext.get(HttpServletResponse.class, HttpServletRequest.class).get(resp);
|
||||
if (req == null) {
|
||||
// Missing the response->request linking... probably in a wrapped instance.
|
||||
return null;
|
||||
}
|
||||
|
||||
final AgentSpan span = startSpan("servlet.response");
|
||||
DECORATE.afterStart(span);
|
||||
|
||||
span.setTag(DDTags.RESOURCE_NAME, "HttpServletResponse." + method);
|
||||
|
||||
// In case we lose context, inject trace into to the request.
|
||||
propagate().inject(span, req, SETTER);
|
||||
|
||||
return activateSpan(span, true).setAsyncPropagation(true);
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
DECORATE.onError(scope, throwable);
|
||||
DECORATE.beforeFinish(scope);
|
||||
scope.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import javax.servlet.Filter
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.FilterConfig
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.ServletRequest
|
||||
import javax.servlet.ServletResponse
|
||||
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.basicSpan
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
class FilterTest extends AgentTestRunner {
|
||||
static {
|
||||
System.setProperty("dd.integration.servlet.enabled", "true")
|
||||
}
|
||||
|
||||
def "test doFilter no-parent"() {
|
||||
when:
|
||||
filter.doFilter(null, null, null)
|
||||
|
||||
then:
|
||||
assertTraces(0) {}
|
||||
|
||||
where:
|
||||
filter = new TestFilter()
|
||||
}
|
||||
|
||||
def "test doFilter with parent"() {
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
filter.doFilter(null, null, null)
|
||||
}
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
basicSpan(it, 0, "parent")
|
||||
span(1) {
|
||||
operationName "servlet.filter"
|
||||
resourceName "${filter.class.simpleName}.doFilter"
|
||||
childOf span(0)
|
||||
tags {
|
||||
"component" "java-web-servlet-filter"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
filter << [new TestFilter(), new TestFilter() {}]
|
||||
}
|
||||
|
||||
def "test doFilter exception"() {
|
||||
setup:
|
||||
def ex = new Exception("some error")
|
||||
def filter = new TestFilter() {
|
||||
@Override
|
||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
filter.doFilter(null, null, null)
|
||||
}
|
||||
|
||||
then:
|
||||
def th = thrown(Exception)
|
||||
th == ex
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
basicSpan(it, 0, "parent", null, ex)
|
||||
span(1) {
|
||||
operationName "servlet.filter"
|
||||
resourceName "${filter.class.simpleName}.doFilter"
|
||||
childOf span(0)
|
||||
errored true
|
||||
tags {
|
||||
"component" "java-web-servlet-filter"
|
||||
defaultTags()
|
||||
errorTags(ex.class, ex.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TestFilter implements Filter {
|
||||
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import groovy.servlet.AbstractHttpServlet
|
||||
import javax.servlet.ServletOutputStream
|
||||
import javax.servlet.http.Cookie
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
import spock.lang.Subject
|
||||
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.basicSpan
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
class HttpServletResponseTest extends AgentTestRunner {
|
||||
static {
|
||||
System.setProperty("dd.integration.servlet.enabled", "true")
|
||||
}
|
||||
|
||||
@Subject
|
||||
def response = new TestResponse()
|
||||
def request = Mock(HttpServletRequest) {
|
||||
getMethod() >> "GET"
|
||||
getProtocol() >> "TEST"
|
||||
}
|
||||
|
||||
def setup() {
|
||||
def servlet = new AbstractHttpServlet() {}
|
||||
// We need to call service so HttpServletAdvice can link the request to the response.
|
||||
servlet.service(request, response)
|
||||
}
|
||||
|
||||
def "test send no-parent"() {
|
||||
when:
|
||||
response.sendError(0)
|
||||
response.sendError(0, "")
|
||||
response.sendRedirect("")
|
||||
|
||||
then:
|
||||
assertTraces(0) {}
|
||||
}
|
||||
|
||||
def "test send with parent"() {
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
response.sendError(0)
|
||||
response.sendError(0, "")
|
||||
response.sendRedirect("")
|
||||
}
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 4) {
|
||||
basicSpan(it, 0, "parent")
|
||||
span(1) {
|
||||
operationName "servlet.response"
|
||||
resourceName "HttpServletResponse.sendRedirect"
|
||||
childOf span(0)
|
||||
tags {
|
||||
"component" "java-web-servlet-response"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "servlet.response"
|
||||
resourceName "HttpServletResponse.sendError"
|
||||
childOf span(0)
|
||||
tags {
|
||||
"component" "java-web-servlet-response"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(3) {
|
||||
operationName "servlet.response"
|
||||
resourceName "HttpServletResponse.sendError"
|
||||
childOf span(0)
|
||||
tags {
|
||||
"component" "java-web-servlet-response"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test send with exception"() {
|
||||
setup:
|
||||
def ex = new Exception("some error")
|
||||
def response = new TestResponse() {
|
||||
@Override
|
||||
void sendRedirect(String s) {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
def servlet = new AbstractHttpServlet() {}
|
||||
// We need to call service so HttpServletAdvice can link the request to the response.
|
||||
servlet.service(request, response)
|
||||
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
response.sendRedirect("")
|
||||
}
|
||||
|
||||
then:
|
||||
def th = thrown(Exception)
|
||||
th == ex
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
basicSpan(it, 0, "parent", null, ex)
|
||||
span(1) {
|
||||
operationName "servlet.response"
|
||||
resourceName "HttpServletResponse.sendRedirect"
|
||||
childOf span(0)
|
||||
errored true
|
||||
tags {
|
||||
"component" "java-web-servlet-response"
|
||||
defaultTags()
|
||||
errorTags(ex.class, ex.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TestResponse implements HttpServletResponse {
|
||||
|
||||
@Override
|
||||
void addCookie(Cookie cookie) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean containsHeader(String s) {
|
||||
return false
|
||||
}
|
||||
|
||||
@Override
|
||||
String encodeURL(String s) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
String encodeRedirectURL(String s) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
String encodeUrl(String s) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
String encodeRedirectUrl(String s) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendError(int i, String s) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendError(int i) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendRedirect(String s) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void setDateHeader(String s, long l) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void addDateHeader(String s, long l) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void setHeader(String s, String s1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void addHeader(String s, String s1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void setIntHeader(String s, int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void addIntHeader(String s, int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void setStatus(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void setStatus(int i, String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
String getCharacterEncoding() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
ServletOutputStream getOutputStream() throws IOException {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
PrintWriter getWriter() throws IOException {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
void setContentLength(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void setContentType(String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void setBufferSize(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
int getBufferSize() {
|
||||
return 0
|
||||
}
|
||||
|
||||
@Override
|
||||
void flushBuffer() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void resetBuffer() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCommitted() {
|
||||
return false
|
||||
}
|
||||
|
||||
@Override
|
||||
void reset() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void setLocale(Locale locale) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
Locale getLocale() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import groovy.servlet.AbstractHttpServlet
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.basicSpan
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
class HttpServletTest extends AgentTestRunner {
|
||||
static {
|
||||
System.setProperty("dd.integration.servlet.enabled", "true")
|
||||
}
|
||||
|
||||
def req = Mock(HttpServletRequest) {
|
||||
getMethod() >> "GET"
|
||||
getProtocol() >> "TEST"
|
||||
}
|
||||
def resp = Mock(HttpServletResponse)
|
||||
|
||||
def "test service no-parent"() {
|
||||
when:
|
||||
servlet.service(req, resp)
|
||||
|
||||
then:
|
||||
assertTraces(0) {}
|
||||
|
||||
where:
|
||||
servlet = new TestServlet()
|
||||
}
|
||||
|
||||
def "test service with parent"() {
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
servlet.service(req, resp)
|
||||
}
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 3) {
|
||||
basicSpan(it, 0, "parent")
|
||||
span(1) {
|
||||
operationName "servlet.service"
|
||||
resourceName "HttpServlet.service"
|
||||
childOf span(0)
|
||||
tags {
|
||||
"component" "java-web-servlet-service"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "servlet.doGet"
|
||||
resourceName "${expectedResourceName}.doGet"
|
||||
childOf span(1)
|
||||
tags {
|
||||
"component" "java-web-servlet-service"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
servlet << [new TestServlet(), new TestServlet() {
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
||||
}
|
||||
}]
|
||||
|
||||
expectedResourceName = servlet.class.anonymousClass ? servlet.class.name : servlet.class.simpleName
|
||||
}
|
||||
|
||||
def "test service exception"() {
|
||||
setup:
|
||||
def ex = new Exception("some error")
|
||||
def servlet = new TestServlet() {
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
servlet.service(req, resp)
|
||||
}
|
||||
|
||||
then:
|
||||
def th = thrown(Exception)
|
||||
th == ex
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 3) {
|
||||
basicSpan(it, 0, "parent", null, ex)
|
||||
span(1) {
|
||||
operationName "servlet.service"
|
||||
resourceName "HttpServlet.service"
|
||||
childOf span(0)
|
||||
errored true
|
||||
tags {
|
||||
"component" "java-web-servlet-service"
|
||||
defaultTags()
|
||||
errorTags(ex.class, ex.message)
|
||||
}
|
||||
}
|
||||
span(2) {
|
||||
operationName "servlet.doGet"
|
||||
resourceName "${servlet.class.name}.doGet"
|
||||
childOf span(1)
|
||||
errored true
|
||||
tags {
|
||||
"component" "java-web-servlet-service"
|
||||
defaultTags()
|
||||
errorTags(ex.class, ex.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TestServlet extends AbstractHttpServlet {
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.basicSpan
|
||||
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
class RequestDispatcherTest extends AgentTestRunner {
|
||||
static {
|
||||
System.setProperty("dd.integration.servlet.enabled", "true")
|
||||
}
|
||||
|
||||
def dispatcher = new RequestDispatcherUtils(Mock(HttpServletRequest), Mock(HttpServletResponse))
|
||||
|
||||
def "test dispatch no-parent"() {
|
||||
when:
|
||||
dispatcher.forward("")
|
||||
dispatcher.include("")
|
||||
|
||||
then:
|
||||
assertTraces(0) {}
|
||||
}
|
||||
|
||||
def "test dispatcher #method with parent"() {
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
dispatcher."$method"(target)
|
||||
}
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
basicSpan(it, 0, "parent")
|
||||
span(1) {
|
||||
operationName "servlet.$operation"
|
||||
resourceName target
|
||||
childOf span(0)
|
||||
tags {
|
||||
"component" "java-web-servlet-dispatcher"
|
||||
defaultTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
operation | method
|
||||
"forward" | "forward"
|
||||
"forward" | "forwardNamed"
|
||||
"include" | "include"
|
||||
"include" | "includeNamed"
|
||||
|
||||
target = "test-$method"
|
||||
}
|
||||
|
||||
def "test dispatcher #method exception"() {
|
||||
setup:
|
||||
def ex = new ServletException("some error")
|
||||
def dispatcher = new RequestDispatcherUtils(Mock(HttpServletRequest), Mock(HttpServletResponse), ex)
|
||||
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
dispatcher."$method"(target)
|
||||
}
|
||||
|
||||
then:
|
||||
def th = thrown(ServletException)
|
||||
th == ex
|
||||
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
basicSpan(it, 0, "parent", null, ex)
|
||||
span(1) {
|
||||
operationName "servlet.$operation"
|
||||
resourceName target
|
||||
childOf span(0)
|
||||
errored true
|
||||
tags {
|
||||
"component" "java-web-servlet-dispatcher"
|
||||
defaultTags()
|
||||
errorTags(ex.class, ex.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
operation | method
|
||||
"forward" | "forward"
|
||||
"forward" | "forwardNamed"
|
||||
"include" | "include"
|
||||
"include" | "includeNamed"
|
||||
|
||||
target = "test-$method"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
public class RequestDispatcherUtils {
|
||||
private final ServletRequest req;
|
||||
private final ServletResponse resp;
|
||||
private final ServletException toThrow;
|
||||
|
||||
public RequestDispatcherUtils(final ServletRequest req, final ServletResponse resp) {
|
||||
this.req = req;
|
||||
this.resp = resp;
|
||||
toThrow = null;
|
||||
}
|
||||
|
||||
public RequestDispatcherUtils(
|
||||
final ServletRequest req, final ServletResponse resp, final ServletException toThrow) {
|
||||
this.req = req;
|
||||
this.resp = resp;
|
||||
this.toThrow = toThrow;
|
||||
}
|
||||
|
||||
/* RequestDispatcher can't be visible to groovy otherwise things break, so everything is
|
||||
* encapsulated in here where groovy doesn't need to access it.
|
||||
*/
|
||||
|
||||
void forward(final String target) throws ServletException, IOException {
|
||||
new TestContext().getRequestDispatcher(target).forward(req, resp);
|
||||
}
|
||||
|
||||
void include(final String target) throws ServletException, IOException {
|
||||
new TestContext().getRequestDispatcher(target).include(req, resp);
|
||||
}
|
||||
|
||||
void forwardNamed(final String target) throws ServletException, IOException {
|
||||
new TestContext().getNamedDispatcher(target).forward(req, resp);
|
||||
}
|
||||
|
||||
void includeNamed(final String target) throws ServletException, IOException {
|
||||
new TestContext().getNamedDispatcher(target).include(req, resp);
|
||||
}
|
||||
|
||||
class TestContext implements ServletContext {
|
||||
@Override
|
||||
public ServletContext getContext(final String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMajorVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinorVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType(final String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getResourcePaths(final String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(final String s) throws MalformedURLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(final String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestDispatcher getRequestDispatcher(final String s) {
|
||||
return new TestDispatcher();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestDispatcher getNamedDispatcher(final String s) {
|
||||
return new TestDispatcher();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Servlet getServlet(final String s) throws ServletException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration getServlets() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration getServletNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(final String s) {}
|
||||
|
||||
@Override
|
||||
public void log(final Exception e, final String s) {}
|
||||
|
||||
@Override
|
||||
public void log(final String s, final Throwable throwable) {}
|
||||
|
||||
@Override
|
||||
public String getRealPath(final String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitParameter(final String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration getInitParameterNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(final String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration getAttributeNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(final String s, final Object o) {}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(final String s) {}
|
||||
|
||||
@Override
|
||||
public String getServletContextName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class TestDispatcher implements RequestDispatcher {
|
||||
@Override
|
||||
public void forward(final ServletRequest servletRequest, final ServletResponse servletResponse)
|
||||
throws ServletException, IOException {
|
||||
if (toThrow != null) {
|
||||
throw toThrow;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void include(final ServletRequest servletRequest, final ServletResponse servletResponse)
|
||||
throws ServletException, IOException {
|
||||
if (toThrow != null) {
|
||||
throw toThrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue