Support Struts 7.0 (fixes CI muzzle failures) (#12935)
Co-authored-by: Lauri Tulmin <ltulmin@splunk.com>
This commit is contained in:
parent
8e449ef233
commit
fa32671be5
|
@ -39,7 +39,7 @@ These are the supported libraries and frameworks:
|
|||
| [Apache Pulsar](https://pulsar.apache.org/) | 2.8+ | N/A | [Messaging Spans] |
|
||||
| [Apache RocketMQ gRPC/Protobuf-based Client](https://rocketmq.apache.org/) | 5.0+ | N/A | [Messaging Spans] |
|
||||
| [Apache RocketMQ Remoting-based Client](https://rocketmq.apache.org/) | 4.8+ | [opentelemetry-rocketmq-client-4.8](../instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/library) | [Messaging Spans] |
|
||||
| [Apache Struts 2](https://github.com/apache/struts) | 2.3+ | N/A | Provides `http.route` [2], Controller Spans [3] |
|
||||
| [Apache Struts](https://github.com/apache/struts) | 2.3+ | N/A | Provides `http.route` [2], Controller Spans [3] |
|
||||
| [Apache Tapestry](https://tapestry.apache.org/) | 5.4+ | N/A | Provides `http.route` [2], Controller Spans [3] |
|
||||
| [Apache Wicket](https://wicket.apache.org/) | 8.0+ | N/A | Provides `http.route` [2] |
|
||||
| [Armeria](https://armeria.dev) | 1.3+ | [opentelemetry-armeria-1.3](../instrumentation/armeria/armeria-1.3/library) | [HTTP Client Spans], [HTTP Client Metrics], [HTTP Server Spans], [HTTP Server Metrics] |
|
||||
|
|
|
@ -6,7 +6,8 @@ muzzle {
|
|||
pass {
|
||||
group.set("org.apache.struts")
|
||||
module.set("struts2-core")
|
||||
versions.set("[2.3.1,)")
|
||||
versions.set("[2.1.0,7)")
|
||||
assertInverse.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +25,7 @@ dependencies {
|
|||
testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent"))
|
||||
testInstrumentation(project(":instrumentation:servlet:servlet-javax-common:javaagent"))
|
||||
testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent"))
|
||||
testInstrumentation(project(":instrumentation:struts:struts-7.0:javaagent"))
|
||||
|
||||
latestDepTestLibrary("org.apache.struts:struts2-core:6.0.+")
|
||||
}
|
|
@ -3,12 +3,12 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts2;
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
|
||||
|
||||
import static io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource.CONTROLLER;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||
import static io.opentelemetry.javaagent.instrumentation.struts2.StrutsSingletons.instrumenter;
|
||||
import static io.opentelemetry.javaagent.instrumentation.struts.v2_3.StrutsSingletons.instrumenter;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts2;
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts2;
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts2;
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
|
||||
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteGetter;
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts2;
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts2;
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest.controller;
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts2;
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServlet;
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts2;
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
|
|
@ -26,16 +26,16 @@
|
|||
<exception-mapping exception="java.lang.Exception" result="error"/>
|
||||
</global-exception-mappings>
|
||||
|
||||
<action name="success" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="success"/>
|
||||
<action name="redirect" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="redirect"/>
|
||||
<action name="query" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="query_param"/>
|
||||
<action name="error-status" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="error"/>
|
||||
<action name="exception" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="exception"/>
|
||||
<action name="/path/{id}/param" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction"
|
||||
<action name="success" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="success"/>
|
||||
<action name="redirect" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="redirect"/>
|
||||
<action name="query" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="query_param"/>
|
||||
<action name="error-status" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="error"/>
|
||||
<action name="exception" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="exception"/>
|
||||
<action name="/path/{id}/param" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction"
|
||||
method="path_param"/>
|
||||
<action name="child" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="indexed_child"/>
|
||||
<action name="captureHeaders" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="capture_headers"/>
|
||||
<action name="dispatch" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="dispatch_servlet"/>
|
||||
<action name="child" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="indexed_child"/>
|
||||
<action name="captureHeaders" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="capture_headers"/>
|
||||
<action name="dispatch" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="dispatch_servlet"/>
|
||||
</package>
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
plugins {
|
||||
id("otel.javaagent-instrumentation")
|
||||
}
|
||||
|
||||
muzzle {
|
||||
pass {
|
||||
group.set("org.apache.struts")
|
||||
module.set("struts2-core")
|
||||
versions.set("[7.0.0,)")
|
||||
assertInverse.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
// struts 7 requires java 17
|
||||
otelJava {
|
||||
minJavaVersionSupported.set(JavaVersion.VERSION_17)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))
|
||||
|
||||
library("org.apache.struts:struts2-core:7.0.0")
|
||||
|
||||
testImplementation(project(":testing-common"))
|
||||
testImplementation("org.eclipse.jetty:jetty-server:11.0.0")
|
||||
testImplementation("org.eclipse.jetty:jetty-servlet:11.0.0")
|
||||
testImplementation("jakarta.servlet:jakarta.servlet-api:5.0.0")
|
||||
testImplementation("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.0.0")
|
||||
|
||||
testInstrumentation(project(":instrumentation:servlet:servlet-5.0:javaagent"))
|
||||
testInstrumentation(project(":instrumentation:jetty:jetty-11.0:javaagent"))
|
||||
testInstrumentation(project(":instrumentation:struts:struts-2.3:javaagent"))
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
|
||||
|
||||
import static io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource.CONTROLLER;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||
import static io.opentelemetry.javaagent.instrumentation.struts.v7_0.StrutsSingletons.instrumenter;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
|
||||
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
|
||||
public class ActionInvocationInstrumentation implements TypeInstrumentation {
|
||||
|
||||
@Override
|
||||
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||
return hasClassesNamed("org.apache.struts2.ActionInvocation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return implementsInterface(named("org.apache.struts2.ActionInvocation"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(TypeTransformer transformer) {
|
||||
transformer.applyAdviceToMethod(
|
||||
isMethod().and(isPublic()).and(named("invokeActionOnly")),
|
||||
this.getClass().getName() + "$InvokeActionOnlyAdvice");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class InvokeActionOnlyAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void onEnter(
|
||||
@Advice.This ActionInvocation actionInvocation,
|
||||
@Advice.Local("otelContext") Context context,
|
||||
@Advice.Local("otelScope") Scope scope) {
|
||||
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||
|
||||
HttpServerRoute.update(
|
||||
parentContext,
|
||||
CONTROLLER,
|
||||
StrutsServerSpanNaming.SERVER_SPAN_NAME,
|
||||
actionInvocation.getProxy());
|
||||
|
||||
if (!instrumenter().shouldStart(parentContext, actionInvocation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
context = instrumenter().start(parentContext, actionInvocation);
|
||||
scope = context.makeCurrent();
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Thrown Throwable throwable,
|
||||
@Advice.This ActionInvocation actionInvocation,
|
||||
@Advice.Local("otelContext") Context context,
|
||||
@Advice.Local("otelScope") Scope scope) {
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
scope.close();
|
||||
|
||||
instrumenter().end(context, actionInvocation, null, throwable);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||
import java.util.List;
|
||||
|
||||
@AutoService(InstrumentationModule.class)
|
||||
public class Struts2InstrumentationModule extends InstrumentationModule {
|
||||
|
||||
public Struts2InstrumentationModule() {
|
||||
super("struts", "struts-7.0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeInstrumentation> typeInstrumentations() {
|
||||
return singletonList(new ActionInvocationInstrumentation());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
|
||||
public class StrutsCodeAttributesGetter implements CodeAttributesGetter<ActionInvocation> {
|
||||
|
||||
@Override
|
||||
public Class<?> getCodeClass(ActionInvocation actionInvocation) {
|
||||
return actionInvocation.getAction().getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodName(ActionInvocation actionInvocation) {
|
||||
return actionInvocation.getProxy().getMethod();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteGetter;
|
||||
import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath;
|
||||
import org.apache.struts2.ActionProxy;
|
||||
|
||||
public class StrutsServerSpanNaming {
|
||||
|
||||
public static final HttpServerRouteGetter<ActionProxy> SERVER_SPAN_NAME =
|
||||
(context, actionProxy) -> {
|
||||
// We take name from the config, because it contains the path pattern from the
|
||||
// configuration.
|
||||
String result = actionProxy.getConfig().getName();
|
||||
|
||||
String actionNamespace = actionProxy.getNamespace();
|
||||
if (actionNamespace != null && !actionNamespace.isEmpty()) {
|
||||
if (actionNamespace.endsWith("/") || result.startsWith("/")) {
|
||||
result = actionNamespace + result;
|
||||
} else {
|
||||
result = actionNamespace + "/" + result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.startsWith("/")) {
|
||||
result = "/" + result;
|
||||
}
|
||||
|
||||
return ServletContextPath.prepend(context, result);
|
||||
};
|
||||
|
||||
private StrutsServerSpanNaming() {}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
|
||||
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
|
||||
public class StrutsSingletons {
|
||||
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.struts-7.0";
|
||||
|
||||
private static final Instrumenter<ActionInvocation, Void> INSTRUMENTER;
|
||||
|
||||
static {
|
||||
StrutsCodeAttributesGetter codeAttributesGetter = new StrutsCodeAttributesGetter();
|
||||
|
||||
INSTRUMENTER =
|
||||
Instrumenter.<ActionInvocation, Void>builder(
|
||||
GlobalOpenTelemetry.get(),
|
||||
INSTRUMENTATION_NAME,
|
||||
CodeSpanNameExtractor.create(codeAttributesGetter))
|
||||
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
|
||||
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
|
||||
.buildInstrumenter();
|
||||
}
|
||||
|
||||
public static Instrumenter<ActionInvocation, Void> instrumenter() {
|
||||
return INSTRUMENTER;
|
||||
}
|
||||
|
||||
private StrutsSingletons() {}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest.controller;
|
||||
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.struts2.ActionSupport;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.interceptor.parameter.StrutsParameter;
|
||||
|
||||
public class GreetingAction extends ActionSupport {
|
||||
|
||||
String responseBody = "default";
|
||||
|
||||
public String success() {
|
||||
responseBody = controller(ServerEndpoint.SUCCESS, ServerEndpoint.SUCCESS::getBody);
|
||||
|
||||
return "greeting";
|
||||
}
|
||||
|
||||
public String redirect() {
|
||||
responseBody = controller(ServerEndpoint.REDIRECT, ServerEndpoint.REDIRECT::getBody);
|
||||
return "redirect";
|
||||
}
|
||||
|
||||
public String query_param() {
|
||||
responseBody = controller(ServerEndpoint.QUERY_PARAM, ServerEndpoint.QUERY_PARAM::getBody);
|
||||
return "greeting";
|
||||
}
|
||||
|
||||
public String error() {
|
||||
controller(ServerEndpoint.ERROR, ServerEndpoint.ERROR::getBody);
|
||||
return "error";
|
||||
}
|
||||
|
||||
public String exception() {
|
||||
controller(
|
||||
ServerEndpoint.EXCEPTION,
|
||||
() -> {
|
||||
throw new IllegalStateException(ServerEndpoint.EXCEPTION.getBody());
|
||||
});
|
||||
throw new AssertionError(); // should not reach here
|
||||
}
|
||||
|
||||
public String path_param() {
|
||||
controller(
|
||||
ServerEndpoint.PATH_PARAM,
|
||||
() ->
|
||||
"this does nothing, as responseBody is set in setId, but we need this controller span nevertheless");
|
||||
return "greeting";
|
||||
}
|
||||
|
||||
public String indexed_child() {
|
||||
responseBody =
|
||||
controller(
|
||||
ServerEndpoint.INDEXED_CHILD,
|
||||
() -> {
|
||||
ServerEndpoint.INDEXED_CHILD.collectSpanAttributes(
|
||||
(name) -> ServletActionContext.getRequest().getParameter(name));
|
||||
return ServerEndpoint.INDEXED_CHILD.getBody();
|
||||
});
|
||||
return "greeting";
|
||||
}
|
||||
|
||||
public String capture_headers() {
|
||||
HttpServletRequest request = ServletActionContext.getRequest();
|
||||
HttpServletResponse response = ServletActionContext.getResponse();
|
||||
response.setHeader("X-Test-Response", request.getHeader("X-Test-Request"));
|
||||
responseBody =
|
||||
controller(ServerEndpoint.CAPTURE_HEADERS, ServerEndpoint.CAPTURE_HEADERS::getBody);
|
||||
return "greeting";
|
||||
}
|
||||
|
||||
public String dispatch_servlet() {
|
||||
return "greetingServlet";
|
||||
}
|
||||
|
||||
@StrutsParameter
|
||||
public void setId(String id) {
|
||||
responseBody = id;
|
||||
}
|
||||
|
||||
public String getResponseBody() {
|
||||
return responseBody;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
|
||||
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GreetingServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
resp.getWriter().write("greeting");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
|
||||
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.api.internal.HttpConstants;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse;
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
class Struts2ActionSpanTest extends AbstractHttpServerTest<Server> {
|
||||
|
||||
@RegisterExtension
|
||||
public static final InstrumentationExtension testing =
|
||||
HttpServerInstrumentationExtension.forAgent();
|
||||
|
||||
@Override
|
||||
protected Server setupServer() throws Exception {
|
||||
Server server = new Server(new InetSocketAddress("localhost", port));
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(null, getContextPath());
|
||||
|
||||
context.addServlet(DefaultServlet.class, "/");
|
||||
context.addServlet(GreetingServlet.class, "/greetingServlet");
|
||||
context.addFilter(
|
||||
StrutsPrepareAndExecuteFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
server.setHandler(context);
|
||||
|
||||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopServer(Server server) throws Exception {
|
||||
server.stop();
|
||||
server.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpServerTestOptions options) {
|
||||
options.setContextPath("/context");
|
||||
options.setTestPathParam(true);
|
||||
options.setTestErrorBody(false);
|
||||
options.setTestPathParam(false);
|
||||
options.setHasHandlerSpan(endpoint -> !endpoint.equals(NOT_FOUND));
|
||||
options.setHasResponseSpan(
|
||||
endpoint ->
|
||||
endpoint == REDIRECT
|
||||
|| endpoint == ERROR
|
||||
|| endpoint == EXCEPTION
|
||||
|| endpoint == NOT_FOUND);
|
||||
|
||||
options.setExpectedHttpRoute(
|
||||
(ServerEndpoint endpoint, String method) -> {
|
||||
if (method.equals(HttpConstants._OTHER)) {
|
||||
return getContextPath() + endpoint.getPath();
|
||||
}
|
||||
if (endpoint.equals(PATH_PARAM)) {
|
||||
return getContextPath() + "/path/{id}/param";
|
||||
} else if (endpoint.equals(NOT_FOUND)) {
|
||||
return getContextPath() + "/*";
|
||||
} else {
|
||||
return super.expectedHttpRoute(endpoint, method);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertResponseSpan(
|
||||
SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) {
|
||||
if (endpoint.equals(REDIRECT)) {
|
||||
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendRedirect"));
|
||||
} else if (endpoint.equals(NOT_FOUND)) {
|
||||
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError"))
|
||||
.hasParent(parentSpan);
|
||||
}
|
||||
|
||||
span.hasKind(SpanKind.INTERNAL);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertHandlerSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
span.hasName("GreetingAction." + endpoint.name().toLowerCase(Locale.ROOT))
|
||||
.hasKind(SpanKind.INTERNAL);
|
||||
|
||||
if (endpoint.equals(EXCEPTION)) {
|
||||
span.hasStatus(StatusData.error())
|
||||
.hasException(new IllegalStateException(EXCEPTION.getBody()));
|
||||
}
|
||||
|
||||
span.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, GreetingAction.class.getName()),
|
||||
equalTo(CODE_FUNCTION, endpoint.name().toLowerCase(Locale.ROOT)));
|
||||
return span;
|
||||
}
|
||||
|
||||
// Struts runs from a servlet filter. Test that dispatching from struts action to a servlet
|
||||
// does not overwrite server span name given by struts instrumentation.
|
||||
@Test
|
||||
void testDispatchToServlet() {
|
||||
AggregatedHttpResponse response =
|
||||
client.get(address.resolve("dispatch").toString()).aggregate().join();
|
||||
|
||||
assertThat(response.status().code()).isEqualTo(200);
|
||||
assertThat(response.contentUtf8()).isEqualTo("greeting");
|
||||
|
||||
testing.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("GET " + getContextPath() + "/dispatch")
|
||||
.hasKind(SpanKind.SERVER)
|
||||
.hasNoParent(),
|
||||
span ->
|
||||
span.hasName("GreetingAction.dispatch_servlet")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
<#-- @ftlvariable name="responseBody" type="java.lang.String" -->
|
||||
${responseBody}
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.5.dtd">
|
||||
|
||||
<struts>
|
||||
|
||||
<package name="basic-struts2" extends="struts-default">
|
||||
<global-results>
|
||||
<result name="redirect" type="redirect">
|
||||
<param name="location">/redirected</param>
|
||||
<param name="prependServletContext">false</param>
|
||||
</result>
|
||||
<result name="error" type="httpheader">
|
||||
<param name="error">500</param>
|
||||
</result>
|
||||
<result type="freemarker" name="greeting">/greeting.ftl</result>
|
||||
<result name="greetingServlet">/greetingServlet</result>
|
||||
</global-results>
|
||||
|
||||
<global-exception-mappings>
|
||||
<exception-mapping exception="java.lang.Exception" result="error"/>
|
||||
</global-exception-mappings>
|
||||
|
||||
<action name="success" class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="success"/>
|
||||
<action name="redirect"
|
||||
class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="redirect"/>
|
||||
<action name="query" class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="query_param"/>
|
||||
<action name="error-status"
|
||||
class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="error"/>
|
||||
<action name="exception"
|
||||
class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="exception"/>
|
||||
<action name="path/{id}/param"
|
||||
class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="path_param"/>
|
||||
<action name="child" class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="indexed_child"/>
|
||||
<action name="captureHeaders"
|
||||
class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="capture_headers"/>
|
||||
<action name="dispatch"
|
||||
class="io.opentelemetry.javaagent.instrumentation.struts.v7_0.GreetingAction"
|
||||
method="dispatch_servlet"/>
|
||||
</package>
|
||||
</struts>
|
|
@ -571,7 +571,8 @@ include(":instrumentation:spring:spring-ws-2.0:javaagent")
|
|||
include(":instrumentation:spring:starters:spring-boot-starter")
|
||||
include(":instrumentation:spring:starters:zipkin-spring-boot-starter")
|
||||
include(":instrumentation:spymemcached-2.12:javaagent")
|
||||
include(":instrumentation:struts-2.3:javaagent")
|
||||
include(":instrumentation:struts:struts-2.3:javaagent")
|
||||
include(":instrumentation:struts:struts-7.0:javaagent")
|
||||
include(":instrumentation:tapestry-5.4:javaagent")
|
||||
include(":instrumentation:tomcat:tomcat-7.0:javaagent")
|
||||
include(":instrumentation:tomcat:tomcat-10.0:javaagent")
|
||||
|
|
Loading…
Reference in New Issue