Skip to content

Commit fa32671

Browse files
trasklaurit
andauthored
Support Struts 7.0 (fixes CI muzzle failures) (#12935)
Co-authored-by: Lauri Tulmin <[email protected]>
1 parent 8e449ef commit fa32671

File tree

24 files changed

+582
-21
lines changed

24 files changed

+582
-21
lines changed

Diff for: docs/supported-libraries.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ These are the supported libraries and frameworks:
3939
| [Apache Pulsar](https://pulsar.apache.org/) | 2.8+ | N/A | [Messaging Spans] |
4040
| [Apache RocketMQ gRPC/Protobuf-based Client](https://rocketmq.apache.org/) | 5.0+ | N/A | [Messaging Spans] |
4141
| [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] |
42-
| [Apache Struts 2](https://github.com/apache/struts) | 2.3+ | N/A | Provides `http.route` [2], Controller Spans [3] |
42+
| [Apache Struts](https://github.com/apache/struts) | 2.3+ | N/A | Provides `http.route` [2], Controller Spans [3] |
4343
| [Apache Tapestry](https://tapestry.apache.org/) | 5.4+ | N/A | Provides `http.route` [2], Controller Spans [3] |
4444
| [Apache Wicket](https://wicket.apache.org/) | 8.0+ | N/A | Provides `http.route` [2] |
4545
| [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] |

Diff for: instrumentation/struts-2.3/javaagent/build.gradle.kts renamed to instrumentation/struts/struts-2.3/javaagent/build.gradle.kts

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ muzzle {
66
pass {
77
group.set("org.apache.struts")
88
module.set("struts2-core")
9-
versions.set("[2.3.1,)")
9+
versions.set("[2.1.0,7)")
10+
assertInverse.set(true)
1011
}
1112
}
1213

@@ -24,6 +25,7 @@ dependencies {
2425
testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent"))
2526
testInstrumentation(project(":instrumentation:servlet:servlet-javax-common:javaagent"))
2627
testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent"))
28+
testInstrumentation(project(":instrumentation:struts:struts-7.0:javaagent"))
2729

2830
latestDepTestLibrary("org.apache.struts:struts2-core:6.0.+")
2931
}

Diff for: instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/ActionInvocationInstrumentation.java renamed to instrumentation/struts/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts/v2_3/ActionInvocationInstrumentation.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.struts2;
6+
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
77

88
import static io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource.CONTROLLER;
99
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
1010
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
11-
import static io.opentelemetry.javaagent.instrumentation.struts2.StrutsSingletons.instrumenter;
11+
import static io.opentelemetry.javaagent.instrumentation.struts.v2_3.StrutsSingletons.instrumenter;
1212
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
1313
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
1414
import static net.bytebuddy.matcher.ElementMatchers.named;

Diff for: instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/Struts2InstrumentationModule.java renamed to instrumentation/struts/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts/v2_3/Struts2InstrumentationModule.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.struts2;
6+
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
77

88
import static java.util.Collections.singletonList;
99

Diff for: instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/StrutsCodeAttributesGetter.java renamed to instrumentation/struts/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts/v2_3/StrutsCodeAttributesGetter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.struts2;
6+
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
77

88
import com.opensymphony.xwork2.ActionInvocation;
99
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;

Diff for: instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/StrutsServerSpanNaming.java renamed to instrumentation/struts/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts/v2_3/StrutsServerSpanNaming.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.struts2;
6+
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
77

88
import com.opensymphony.xwork2.ActionProxy;
99
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteGetter;

Diff for: instrumentation/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts2/StrutsSingletons.java renamed to instrumentation/struts/struts-2.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/struts/v2_3/StrutsSingletons.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.struts2;
6+
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
77

88
import com.opensymphony.xwork2.ActionInvocation;
99
import io.opentelemetry.api.GlobalOpenTelemetry;

Diff for: instrumentation/struts-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/struts2/GreetingAction.java renamed to instrumentation/struts/struts-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/struts/v2_3/GreetingAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.struts2;
6+
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
77

88
import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest.controller;
99

Diff for: instrumentation/struts-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/struts2/GreetingServlet.java renamed to instrumentation/struts/struts-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/struts/v2_3/GreetingServlet.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.struts2;
6+
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
77

88
import java.io.IOException;
99
import javax.servlet.http.HttpServlet;

Diff for: instrumentation/struts-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/struts2/Struts2ActionSpanTest.java renamed to instrumentation/struts/struts-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/struts/v2_3/Struts2ActionSpanTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package io.opentelemetry.javaagent.instrumentation.struts2;
6+
package io.opentelemetry.javaagent.instrumentation.struts.v2_3;
77

88
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR;
99
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;

Diff for: instrumentation/struts-2.3/javaagent/src/test/resources/struts.xml renamed to instrumentation/struts/struts-2.3/javaagent/src/test/resources/struts.xml

+9-9
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@
2626
<exception-mapping exception="java.lang.Exception" result="error"/>
2727
</global-exception-mappings>
2828

29-
<action name="success" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="success"/>
30-
<action name="redirect" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="redirect"/>
31-
<action name="query" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="query_param"/>
32-
<action name="error-status" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="error"/>
33-
<action name="exception" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="exception"/>
34-
<action name="/path/{id}/param" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction"
29+
<action name="success" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="success"/>
30+
<action name="redirect" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="redirect"/>
31+
<action name="query" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="query_param"/>
32+
<action name="error-status" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="error"/>
33+
<action name="exception" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="exception"/>
34+
<action name="/path/{id}/param" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction"
3535
method="path_param"/>
36-
<action name="child" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="indexed_child"/>
37-
<action name="captureHeaders" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="capture_headers"/>
38-
<action name="dispatch" class="io.opentelemetry.javaagent.instrumentation.struts2.GreetingAction" method="dispatch_servlet"/>
36+
<action name="child" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="indexed_child"/>
37+
<action name="captureHeaders" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="capture_headers"/>
38+
<action name="dispatch" class="io.opentelemetry.javaagent.instrumentation.struts.v2_3.GreetingAction" method="dispatch_servlet"/>
3939
</package>
4040

4141

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("org.apache.struts")
8+
module.set("struts2-core")
9+
versions.set("[7.0.0,)")
10+
assertInverse.set(true)
11+
}
12+
}
13+
14+
// struts 7 requires java 17
15+
otelJava {
16+
minJavaVersionSupported.set(JavaVersion.VERSION_17)
17+
}
18+
19+
dependencies {
20+
bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))
21+
22+
library("org.apache.struts:struts2-core:7.0.0")
23+
24+
testImplementation(project(":testing-common"))
25+
testImplementation("org.eclipse.jetty:jetty-server:11.0.0")
26+
testImplementation("org.eclipse.jetty:jetty-servlet:11.0.0")
27+
testImplementation("jakarta.servlet:jakarta.servlet-api:5.0.0")
28+
testImplementation("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.0.0")
29+
30+
testInstrumentation(project(":instrumentation:servlet:servlet-5.0:javaagent"))
31+
testInstrumentation(project(":instrumentation:jetty:jetty-11.0:javaagent"))
32+
testInstrumentation(project(":instrumentation:struts:struts-2.3:javaagent"))
33+
}
34+
35+
tasks.withType<Test>().configureEach {
36+
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
7+
8+
import static io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource.CONTROLLER;
9+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
10+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
11+
import static io.opentelemetry.javaagent.instrumentation.struts.v7_0.StrutsSingletons.instrumenter;
12+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
13+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
14+
import static net.bytebuddy.matcher.ElementMatchers.named;
15+
16+
import io.opentelemetry.context.Context;
17+
import io.opentelemetry.context.Scope;
18+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
19+
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
20+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
21+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
22+
import net.bytebuddy.asm.Advice;
23+
import net.bytebuddy.description.type.TypeDescription;
24+
import net.bytebuddy.matcher.ElementMatcher;
25+
import org.apache.struts2.ActionInvocation;
26+
27+
public class ActionInvocationInstrumentation implements TypeInstrumentation {
28+
29+
@Override
30+
public ElementMatcher<ClassLoader> classLoaderOptimization() {
31+
return hasClassesNamed("org.apache.struts2.ActionInvocation");
32+
}
33+
34+
@Override
35+
public ElementMatcher<TypeDescription> typeMatcher() {
36+
return implementsInterface(named("org.apache.struts2.ActionInvocation"));
37+
}
38+
39+
@Override
40+
public void transform(TypeTransformer transformer) {
41+
transformer.applyAdviceToMethod(
42+
isMethod().and(isPublic()).and(named("invokeActionOnly")),
43+
this.getClass().getName() + "$InvokeActionOnlyAdvice");
44+
}
45+
46+
@SuppressWarnings("unused")
47+
public static class InvokeActionOnlyAdvice {
48+
49+
@Advice.OnMethodEnter(suppress = Throwable.class)
50+
public static void onEnter(
51+
@Advice.This ActionInvocation actionInvocation,
52+
@Advice.Local("otelContext") Context context,
53+
@Advice.Local("otelScope") Scope scope) {
54+
Context parentContext = Java8BytecodeBridge.currentContext();
55+
56+
HttpServerRoute.update(
57+
parentContext,
58+
CONTROLLER,
59+
StrutsServerSpanNaming.SERVER_SPAN_NAME,
60+
actionInvocation.getProxy());
61+
62+
if (!instrumenter().shouldStart(parentContext, actionInvocation)) {
63+
return;
64+
}
65+
66+
context = instrumenter().start(parentContext, actionInvocation);
67+
scope = context.makeCurrent();
68+
}
69+
70+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
71+
public static void stopSpan(
72+
@Advice.Thrown Throwable throwable,
73+
@Advice.This ActionInvocation actionInvocation,
74+
@Advice.Local("otelContext") Context context,
75+
@Advice.Local("otelScope") Scope scope) {
76+
if (scope == null) {
77+
return;
78+
}
79+
scope.close();
80+
81+
instrumenter().end(context, actionInvocation, null, throwable);
82+
}
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
7+
8+
import static java.util.Collections.singletonList;
9+
10+
import com.google.auto.service.AutoService;
11+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import java.util.List;
14+
15+
@AutoService(InstrumentationModule.class)
16+
public class Struts2InstrumentationModule extends InstrumentationModule {
17+
18+
public Struts2InstrumentationModule() {
19+
super("struts", "struts-7.0");
20+
}
21+
22+
@Override
23+
public List<TypeInstrumentation> typeInstrumentations() {
24+
return singletonList(new ActionInvocationInstrumentation());
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
7+
8+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
9+
import org.apache.struts2.ActionInvocation;
10+
11+
public class StrutsCodeAttributesGetter implements CodeAttributesGetter<ActionInvocation> {
12+
13+
@Override
14+
public Class<?> getCodeClass(ActionInvocation actionInvocation) {
15+
return actionInvocation.getAction().getClass();
16+
}
17+
18+
@Override
19+
public String getMethodName(ActionInvocation actionInvocation) {
20+
return actionInvocation.getProxy().getMethod();
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
7+
8+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteGetter;
9+
import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath;
10+
import org.apache.struts2.ActionProxy;
11+
12+
public class StrutsServerSpanNaming {
13+
14+
public static final HttpServerRouteGetter<ActionProxy> SERVER_SPAN_NAME =
15+
(context, actionProxy) -> {
16+
// We take name from the config, because it contains the path pattern from the
17+
// configuration.
18+
String result = actionProxy.getConfig().getName();
19+
20+
String actionNamespace = actionProxy.getNamespace();
21+
if (actionNamespace != null && !actionNamespace.isEmpty()) {
22+
if (actionNamespace.endsWith("/") || result.startsWith("/")) {
23+
result = actionNamespace + result;
24+
} else {
25+
result = actionNamespace + "/" + result;
26+
}
27+
}
28+
29+
if (!result.startsWith("/")) {
30+
result = "/" + result;
31+
}
32+
33+
return ServletContextPath.prepend(context, result);
34+
};
35+
36+
private StrutsServerSpanNaming() {}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.struts.v7_0;
7+
8+
import io.opentelemetry.api.GlobalOpenTelemetry;
9+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
10+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
11+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
12+
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
13+
import org.apache.struts2.ActionInvocation;
14+
15+
public class StrutsSingletons {
16+
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.struts-7.0";
17+
18+
private static final Instrumenter<ActionInvocation, Void> INSTRUMENTER;
19+
20+
static {
21+
StrutsCodeAttributesGetter codeAttributesGetter = new StrutsCodeAttributesGetter();
22+
23+
INSTRUMENTER =
24+
Instrumenter.<ActionInvocation, Void>builder(
25+
GlobalOpenTelemetry.get(),
26+
INSTRUMENTATION_NAME,
27+
CodeSpanNameExtractor.create(codeAttributesGetter))
28+
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
29+
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
30+
.buildInstrumenter();
31+
}
32+
33+
public static Instrumenter<ActionInvocation, Void> instrumenter() {
34+
return INSTRUMENTER;
35+
}
36+
37+
private StrutsSingletons() {}
38+
}

0 commit comments

Comments
 (0)