diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index aa173229d643..29c10880ecc9 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -51,7 +51,7 @@ These are the supported libraries and frameworks: | [Eclipse Grizzly](https://javaee.github.io/grizzly/httpserverframework.html) | 2.3+ | N/A | [HTTP Server Spans], [HTTP Server Metrics] | | [Eclipse Jersey](https://eclipse-ee4j.github.io/jersey/) | 2.0+ (not including 3.x yet) | N/A | Provides `http.route` [2], Controller Spans [3] | | [Eclipse Jetty HTTP Client](https://www.eclipse.org/jetty/javadoc/jetty-9/org/eclipse/jetty/client/HttpClient.html) | 9.2+ (not including 10+ yet) | [opentelemetry-jetty-httpclient-9.2](../instrumentation/jetty-httpclient/jetty-httpclient-9.2/library) | [HTTP Client Spans], [HTTP Client Metrics] | -| [Eclipse Metro](https://projects.eclipse.org/projects/ee4j.metro) | 2.2+ (not including 3.x yet) | N/A | Provides `http.route` [2], Controller Spans [3] | +| [Eclipse Metro](https://projects.eclipse.org/projects/ee4j.metro) | 2.2+ | N/A | Provides `http.route` [2], Controller Spans [3] | | [Eclipse Mojarra](https://projects.eclipse.org/projects/ee4j.mojarra) | 1.2+ (not including 3.x yet) | N/A | Provides `http.route` [2], Controller Spans [3] | | [Elasticsearch API Client](https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html) | 7.16+ and 8.0+ | N/A | [Elasticsearch Client Spans] | | [Elasticsearch REST Client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html) | 5.0+ | N/A | [Database Client Spans] | diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/build.gradle.kts new file mode 100644 index 000000000000..9dd6990cf05e --- /dev/null +++ b/instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("otel.javaagent-testing") +} + +dependencies { + testImplementation("javax.servlet:javax.servlet-api:3.0.1") + testImplementation("com.sun.xml.ws:jaxws-rt:2.3.6") + + testImplementation(project(":instrumentation:jaxws:jaxws-2.0-common-testing")) + + testInstrumentation(project(":instrumentation:jaxws:jaxws-metro-2.2:javaagent")) + testInstrumentation(project(":instrumentation:jaxws:jaxws-2.0:javaagent")) + testInstrumentation(project(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent")) + + testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) + testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent")) + + latestDepTestLibrary("com.sun.xml.ws:jaxws-rt:2.+") + latestDepTestLibrary("com.sun.xml.stream.buffer:streambuffer:1.+") +} + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-exports=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED") + jvmArgs("--add-exports=java.xml/com.sun.org.apache.xerces.internal.jaxp=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/test/groovy/MetroJaxWsTest.groovy b/instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/src/test/groovy/MetroJaxWsTest.groovy similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/test/groovy/MetroJaxWsTest.groovy rename to instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/src/test/groovy/MetroJaxWsTest.groovy diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/test/resources/test-app/WEB-INF/sun-jaxws.xml b/instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/sun-jaxws.xml similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/test/resources/test-app/WEB-INF/sun-jaxws.xml rename to instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/sun-jaxws.xml diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/test/resources/test-app/WEB-INF/web.xml b/instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/web.xml similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/test/resources/test-app/WEB-INF/web.xml rename to instrumentation/jaxws/jaxws-2.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/web.xml diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroServerSpanNaming.java b/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroServerSpanNaming.java deleted file mode 100644 index 284cbf8ed620..000000000000 --- a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroServerSpanNaming.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.metro; - -import com.sun.xml.ws.api.message.Packet; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan; -import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath; -import javax.servlet.http.HttpServletRequest; -import javax.xml.ws.handler.MessageContext; - -public final class MetroServerSpanNaming { - - public static void updateServerSpanName(Context context, MetroRequest metroRequest) { - String spanName = metroRequest.spanName(); - if (spanName == null) { - return; - } - - Span serverSpan = LocalRootSpan.fromContextOrNull(context); - if (serverSpan == null) { - return; - } - - Packet packet = metroRequest.packet(); - if (packet.supports(MessageContext.SERVLET_REQUEST)) { - Object request = packet.get(MessageContext.SERVLET_REQUEST); - if (request instanceof HttpServletRequest) { - HttpServletRequest httpRequest = (HttpServletRequest) request; - String servletPath = httpRequest.getServletPath(); - if (!servletPath.isEmpty()) { - String pathInfo = httpRequest.getPathInfo(); - if (pathInfo != null) { - spanName = servletPath + "/" + spanName; - } else { - // when pathInfo is null then there is a servlet that is mapped to this exact service - // servletPath already contains the service name - String operationName = packet.getWSDLOperation().getLocalPart(); - spanName = servletPath + "/" + operationName; - } - } - } - } - - serverSpan.updateName(ServletContextPath.prepend(context, spanName)); - } - - private MetroServerSpanNaming() {} -} diff --git a/instrumentation/jaxws/jaxws-3.0-common-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-3.0-common-testing/build.gradle.kts new file mode 100644 index 000000000000..ecd8f75a5c37 --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-common-testing/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("org.unbroken-dome.xjc") + id("otel.java-conventions") +} + +tasks { + named("checkstyleMain") { + // exclude generated web service classes + exclude("**/hello_web_service/**") + } +} + +dependencies { + api("jakarta.xml.ws:jakarta.xml.ws-api:3.0.0") + api("jakarta.jws:jakarta.jws-api:3.0.0") + + api("ch.qos.logback:logback-classic") + api("org.slf4j:log4j-over-slf4j") + api("org.slf4j:jcl-over-slf4j") + api("org.slf4j:jul-to-slf4j") + api("org.eclipse.jetty:jetty-webapp:11.0.17") + api("org.springframework.ws:spring-ws-core:4.0.0") + + implementation(project(":testing-common")) + + xjcTool("com.sun.xml.bind:jaxb-xjc:3.0.2") + xjcTool("com.sun.xml.bind:jaxb-impl:3.0.2") +} diff --git a/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/AbstractJaxWsTest.groovy b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/AbstractJaxWsTest.groovy new file mode 100644 index 000000000000..b1ce89abb00d --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/AbstractJaxWsTest.groovy @@ -0,0 +1,187 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.instrumentation.test.asserts.TraceAssert +import io.opentelemetry.instrumentation.test.base.HttpServerTestTrait +import io.opentelemetry.sdk.trace.data.SpanData +import io.opentelemetry.semconv.SemanticAttributes +import io.opentelemetry.test.hello_web_service.Hello2Request +import io.opentelemetry.test.hello_web_service.HelloRequest +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.util.resource.Resource +import org.eclipse.jetty.webapp.WebAppContext +import org.springframework.oxm.jaxb.Jaxb2Marshaller +import org.springframework.util.ClassUtils +import org.springframework.ws.client.core.WebServiceTemplate +import org.springframework.ws.soap.client.SoapFaultClientException +import spock.lang.Shared +import spock.lang.Unroll + +import static io.opentelemetry.api.trace.SpanKind.INTERNAL +import static io.opentelemetry.api.trace.SpanKind.SERVER +import static io.opentelemetry.api.trace.StatusCode.ERROR + +abstract class AbstractJaxWsTest extends AgentInstrumentationSpecification implements HttpServerTestTrait { + + @Shared + private Jaxb2Marshaller marshaller = new Jaxb2Marshaller() + + @Shared + protected WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller) + + def setupSpec() { + setupServer() + + marshaller.setPackagesToScan(ClassUtils.getPackageName(HelloRequest)) + marshaller.afterPropertiesSet() + } + + def cleanupSpec() { + cleanupServer() + } + + @Override + Server startServer(int port) { + WebAppContext webAppContext = new WebAppContext() + webAppContext.setContextPath(getContextPath()) + // set up test application + webAppContext.setBaseResource(Resource.newSystemResource("test-app")) + webAppContext.getMetaData().addWebInfResource(Resource.newClassPathResource("/")) + + def jettyServer = new Server(port) + jettyServer.connectors.each { + it.setHost('localhost') + } + + jettyServer.setHandler(webAppContext) + jettyServer.start() + + return jettyServer + } + + @Override + void stopServer(Server server) { + server.stop() + server.destroy() + } + + @Override + String getContextPath() { + return "/jetty-context" + } + + String getServiceAddress(String serviceName) { + return address.resolve("ws/" + serviceName).toString() + } + + def makeRequest(methodName, name) { + Object request = null + if ("hello" == methodName) { + request = new HelloRequest(name: name) + } else if ("hello2" == methodName) { + request = new Hello2Request(name: name) + } else { + throw new IllegalArgumentException(methodName) + } + + return webServiceTemplate.marshalSendAndReceive(getServiceAddress("HelloService"), request) + } + + @Unroll + def "test #methodName"() { + setup: + def response = makeRequest(methodName, "Test") + + expect: + response.getMessage() == "Hello Test" + + and: + def spanCount = 2 + assertTraces(1) { + trace(0, spanCount) { + serverSpan(it, 0, serverSpanName(methodName)) + handlerSpan(it, 1, methodName, span(0)) + } + } + + where: + methodName << ["hello", "hello2"] + } + + @Unroll + def "test #methodName exception"() { + when: + makeRequest(methodName, "exception") + + then: + def error = thrown(SoapFaultClientException) + error.getMessage() == "hello exception" + + and: + def spanCount = 2 + def expectedException = new Exception("hello exception") + assertTraces(1) { + trace(0, spanCount) { + serverSpan(it, 0, serverSpanName(methodName), expectedException) + handlerSpan(it, 1, methodName, span(0), expectedException) + } + } + + where: + methodName << ["hello", "hello2"] + } + + def serverSpanName(String operation) { + return getContextPath() + "/ws/HelloService/" + operation + } + + static serverSpan(TraceAssert trace, int index, String operation, Throwable exception = null) { + trace.span(index) { + hasNoParent() + name operation + kind SERVER + if (exception != null) { + status ERROR + } + } + } + + static handlerSpan(TraceAssert trace, int index, String operation, Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + if (parentSpan == null) { + hasNoParent() + } else { + childOf((SpanData) parentSpan) + } + name "HelloService/" + operation + kind INTERNAL + if (exception) { + status ERROR + errorEvent(exception.class, exception.message) + } + } + } + + static annotationHandlerSpan(TraceAssert trace, int index, String methodName, Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + if (parentSpan == null) { + hasNoParent() + } else { + childOf((SpanData) parentSpan) + } + name "HelloServiceImpl." + methodName + kind INTERNAL + if (exception) { + status ERROR + errorEvent(exception.class, exception.message) + } + attributes { + "$SemanticAttributes.CODE_NAMESPACE" "hello.HelloServiceImpl" + "$SemanticAttributes.CODE_FUNCTION" methodName + } + } + } +} diff --git a/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/BaseHelloService.groovy b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/BaseHelloService.groovy new file mode 100644 index 000000000000..a2d8da1e8a11 --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/BaseHelloService.groovy @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package hello + +class BaseHelloService { + + String hello2(String name) { + if ("exception" == name) { + throw new Exception("hello exception") + } + return "Hello " + name + } +} diff --git a/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/HelloService.groovy b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/HelloService.groovy new file mode 100644 index 000000000000..6e79845e2063 --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/HelloService.groovy @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package hello + +import jakarta.jws.WebParam +import jakarta.jws.WebResult +import jakarta.jws.WebService +import jakarta.xml.ws.RequestWrapper + +@WebService(targetNamespace = "http://opentelemetry.io/test/hello-web-service") +interface HelloService { + + @RequestWrapper(localName = "helloRequest") + @WebResult(name = "message") + String hello(@WebParam(name = "name") String name) + + @RequestWrapper(localName = "hello2Request") + @WebResult(name = "message") + String hello2(@WebParam(name = "name") String name) + +} diff --git a/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/HelloServiceImpl.groovy b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/HelloServiceImpl.groovy new file mode 100644 index 000000000000..5a54f9e42021 --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/groovy/hello/HelloServiceImpl.groovy @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package hello + +import jakarta.jws.WebService + +@WebService(serviceName = "HelloService", endpointInterface = "hello.HelloService", targetNamespace = "http://opentelemetry.io/test/hello-web-service") +class HelloServiceImpl extends BaseHelloService implements HelloService { + + String hello(String name) { + if ("exception" == name) { + throw new Exception("hello exception") + } + return "Hello " + name + } +} diff --git a/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/schema/hello.xsd b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/schema/hello.xsd new file mode 100644 index 000000000000..f46d7d715238 --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-common-testing/src/main/schema/hello.xsd @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/build.gradle.kts b/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/build.gradle.kts new file mode 100644 index 000000000000..7ac00df018ba --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("otel.javaagent-testing") +} + +dependencies { + testImplementation("jakarta.servlet:jakarta.servlet-api:5.0.0") + testImplementation("com.sun.xml.ws:jaxws-rt:3.0.0") + + testImplementation(project(":instrumentation:jaxws:jaxws-3.0-common-testing")) + + testInstrumentation(project(":instrumentation:jaxws:jaxws-metro-2.2:javaagent")) + + testInstrumentation(project(":instrumentation:servlet:servlet-5.0:javaagent")) + testInstrumentation(project(":instrumentation:jetty:jetty-11.0:javaagent")) +} + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-exports=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED") + jvmArgs("--add-exports=java.xml/com.sun.org.apache.xerces.internal.jaxp=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/groovy/MetroJaxWsTest.groovy b/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/groovy/MetroJaxWsTest.groovy new file mode 100644 index 000000000000..c20abd4456f3 --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/groovy/MetroJaxWsTest.groovy @@ -0,0 +1,7 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +class MetroJaxWsTest extends AbstractJaxWsTest { +} diff --git a/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/sun-jaxws.xml b/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/sun-jaxws.xml new file mode 100644 index 000000000000..6bf3e05acba2 --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/sun-jaxws.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/web.xml b/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/web.xml new file mode 100644 index 000000000000..037ef81d3bd2 --- /dev/null +++ b/instrumentation/jaxws/jaxws-3.0-metro-2.2-testing/src/test/resources/test-app/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + + + com.sun.xml.ws.transport.http.servlet.WSServletContextListener + + + + WSServlet + com.sun.xml.ws.transport.http.servlet.WSServlet + 1 + + + + WSServlet + /ws/* + + diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/build.gradle.kts b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/build.gradle.kts similarity index 58% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/build.gradle.kts rename to instrumentation/jaxws/jaxws-metro-2.2/javaagent/build.gradle.kts index 59be6661ab3e..aebdda4d0f53 100644 --- a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/build.gradle.kts +++ b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/build.gradle.kts @@ -6,11 +6,10 @@ muzzle { pass { group.set("com.sun.xml.ws") module.set("jaxws-rt") - versions.set("[2.2.0.1,3)") + versions.set("[2.2.0.1,)") // version 2.3.4 depends on org.glassfish.gmbal:gmbal-api-only:4.0.3 which does not exist skip("2.3.4") assertInverse.set(true) - extraDependency("javax.servlet:javax.servlet-api:3.0.1") } } @@ -22,18 +21,8 @@ dependencies { // which doesn't work with java 8 library("com.sun.xml.stream.buffer:streambuffer:1.4") - compileOnly("javax.servlet:javax.servlet-api:3.0.1") - - testImplementation(project(":instrumentation:jaxws:jaxws-2.0-common-testing")) - - testInstrumentation(project(":instrumentation:jaxws:jaxws-2.0:javaagent")) - testInstrumentation(project(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent")) - - testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) - testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent")) - - latestDepTestLibrary("com.sun.xml.ws:jaxws-rt:2.+") - latestDepTestLibrary("com.sun.xml.stream.buffer:streambuffer:1.+") + compileOnly("javax.xml.ws:jaxws-api:2.0") + compileOnly("jakarta.xml.ws:jakarta.xml.ws-api:3.0.0") } tasks.withType().configureEach { diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroHelper.java b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroHelper.java similarity index 92% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroHelper.java rename to instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroHelper.java index b2122c29e2b0..8c8ad4996b93 100644 --- a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroHelper.java +++ b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroHelper.java @@ -18,13 +18,16 @@ public final class MetroHelper { private static final String SCOPE_KEY = MetroHelper.class.getName() + ".Scope"; private static final String THROWABLE_KEY = MetroHelper.class.getName() + ".Throwable"; + private static final MetroServerSpanNameUpdater SPAN_NAME_UPDATER = + new MetroServerSpanNameUpdater(); + private MetroHelper() {} public static void start(WSEndpoint endpoint, Packet packet) { Context parentContext = Context.current(); MetroRequest request = new MetroRequest(endpoint, packet); - MetroServerSpanNaming.updateServerSpanName(parentContext, request); + SPAN_NAME_UPDATER.updateServerSpanName(parentContext, request); if (!instrumenter().shouldStart(parentContext, request)) { return; diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroInstrumentationModule.java b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroInstrumentationModule.java similarity index 92% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroInstrumentationModule.java rename to instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroInstrumentationModule.java index 957dbf04537a..2a39fe143e3e 100644 --- a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroInstrumentationModule.java +++ b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroInstrumentationModule.java @@ -22,7 +22,7 @@ public MetroInstrumentationModule() { @Override public ElementMatcher.Junction classLoaderMatcher() { - return hasClassesNamed("javax.jws.WebService"); + return hasClassesNamed("com.sun.xml.ws.api.pipe.ServerTubeAssemblerContext"); } @Override diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroRequest.java b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroRequest.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroRequest.java rename to instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroRequest.java diff --git a/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroServerSpanNameUpdater.java b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroServerSpanNameUpdater.java new file mode 100644 index 000000000000..3998099794c4 --- /dev/null +++ b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroServerSpanNameUpdater.java @@ -0,0 +1,163 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.metro; + +import com.sun.xml.ws.api.message.Packet; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan; +import io.opentelemetry.javaagent.bootstrap.servlet.ServletContextPath; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Logger; + +final class MetroServerSpanNameUpdater { + + private static final Logger logger = Logger.getLogger(MetroServerSpanNameUpdater.class.getName()); + + /** + * Map of message context key names to the {@link HttpServletRequestAdapter} to handle the {@code + * HttpServletRequest} found at that message context key. + * + *

This map will contain at most two entries: + * + *

    + *
  • {@value javax.xml.ws.handler.MessageContext#SERVLET_REQUEST} to an {@link + * HttpServletRequestAdapter} that handles {@code javax.servlet.http.HttpServletRequest} + *
  • {@value jakarta.xml.ws.handler.MessageContext#SERVLET_REQUEST} to an {@link + * HttpServletRequestAdapter} that handles {@code jakarta.servlet.http.HttpServletRequest} + *
+ */ + private final Map servletRequestAdapters; + + public MetroServerSpanNameUpdater() { + this.servletRequestAdapters = new LinkedHashMap<>(); + + registerHttpServletRequestAdapter( + "Jakarta EE", + // Same as jakarta.xml.ws.handler.MessageContext.SERVLET_REQUEST + "jakarta.xml.ws.servlet.request", + "jakarta.servlet.http.HttpServletRequest"); + + registerHttpServletRequestAdapter( + "Java EE", + // Same as javax.xml.ws.handler.MessageContext.SERVLET_REQUEST + "javax.xml.ws.servlet.request", + "javax.servlet.http.HttpServletRequest"); + } + + /** + * Registers a {@link HttpServletRequestAdapter} in the {@link #servletRequestAdapters} with the + * given {@code key} if the given {@code httpServletRequestClassName} is on the classpath. + */ + private void registerHttpServletRequestAdapter( + String name, String key, String httpServletRequestClassName) { + final HttpServletRequestAdapter adapter; + try { + adapter = + new HttpServletRequestAdapter( + Class.forName(httpServletRequestClassName, true, getClass().getClassLoader())); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + // Ignore. Don't register + return; + } + servletRequestAdapters.put(key, adapter); + logger.finest(() -> "Enabled " + name + " jaxws metro server span naming"); + } + + public void updateServerSpanName(Context context, MetroRequest metroRequest) { + String spanName = metroRequest.spanName(); + if (spanName == null) { + return; + } + + Span serverSpan = LocalRootSpan.fromContextOrNull(context); + if (serverSpan == null) { + return; + } + + for (Map.Entry httpServletRequestAdapterEntry : + servletRequestAdapters.entrySet()) { + Packet packet = metroRequest.packet(); + String key = httpServletRequestAdapterEntry.getKey(); + if (packet.supports(key)) { + Object request = packet.get(key); + HttpServletRequestAdapter httpServletRequestAdapter = + httpServletRequestAdapterEntry.getValue(); + if (httpServletRequestAdapter.canHandle(request)) { + String servletPath = httpServletRequestAdapter.getServletPath(request); + if (!servletPath.isEmpty()) { + String pathInfo = httpServletRequestAdapter.getPathInfo(request); + if (pathInfo != null) { + spanName = servletPath + "/" + spanName; + } else { + // when pathInfo is null then there is a servlet that is mapped to this exact service + // servletPath already contains the service name + String operationName = packet.getWSDLOperation().getLocalPart(); + spanName = servletPath + "/" + operationName; + } + break; + } + } + } + } + + serverSpan.updateName(ServletContextPath.prepend(context, spanName)); + } + + /** + * Adapter class for accessing the methods needed from either {@code + * jakarta.servlet.http.HttpServletRequest} or {@code javax.servlet.http.HttpServletRequest}. + */ + private static class HttpServletRequestAdapter { + + private final Class httpServletRequestClass; + private final MethodHandle getServletPathMethodHandle; + private final MethodHandle getPathInfoMethodHandle; + + private HttpServletRequestAdapter(Class httpServletRequestClass) + throws NoSuchMethodException, IllegalAccessException { + this.httpServletRequestClass = + Objects.requireNonNull( + httpServletRequestClass, "httpServletRequestClass must not be null"); + + MethodHandles.Lookup lookup = MethodHandles.lookup(); + this.getServletPathMethodHandle = + lookup.unreflect(httpServletRequestClass.getMethod("getServletPath")); + this.getPathInfoMethodHandle = + lookup.unreflect(httpServletRequestClass.getMethod("getPathInfo")); + } + + public boolean canHandle(Object httpServletRequest) { + return httpServletRequestClass.isInstance(httpServletRequest); + } + + public String getServletPath(Object httpServletRequest) { + return invokeSafely(getServletPathMethodHandle, httpServletRequest); + } + + public String getPathInfo(Object httpServletRequest) { + return invokeSafely(getPathInfoMethodHandle, httpServletRequest); + } + + private static String invokeSafely(MethodHandle methodHandle, Object httpServletRequest) { + try { + return (String) methodHandle.invoke(httpServletRequest); + } catch (RuntimeException | Error e) { + throw e; + } catch (Throwable t) { + /* + * This is impossible, because the methods being invoked do not throw checked exceptions, + * and unchecked exceptions and errors are handled above + */ + throw new AssertionError(t); + } + } + } +} diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java similarity index 96% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java rename to instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java index 470bccf76cc9..aa6ef3858d56 100644 --- a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java +++ b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/MetroSingletons.java @@ -10,7 +10,7 @@ import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; public class MetroSingletons { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jaxws-2.0-metro-2.2"; + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jaxws-metro-2.2"; private static final Instrumenter INSTRUMENTER; diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/ServerTubeAssemblerContextInstrumentation.java b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/ServerTubeAssemblerContextInstrumentation.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/ServerTubeAssemblerContextInstrumentation.java rename to instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/ServerTubeAssemblerContextInstrumentation.java diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/SoapFaultBuilderInstrumentation.java b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/SoapFaultBuilderInstrumentation.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/SoapFaultBuilderInstrumentation.java rename to instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/SoapFaultBuilderInstrumentation.java diff --git a/instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/TracingTube.java b/instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/TracingTube.java similarity index 100% rename from instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/TracingTube.java rename to instrumentation/jaxws/jaxws-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/metro/TracingTube.java diff --git a/settings.gradle.kts b/settings.gradle.kts index 88c453fca3c0..0656f54332db 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -287,15 +287,18 @@ include(":instrumentation:jaxrs:jaxrs-3.0:jaxrs-3.0-jersey-3.0:javaagent") include(":instrumentation:jaxrs:jaxrs-3.0:jaxrs-3.0-resteasy-6.0:javaagent") include(":instrumentation:jaxrs-client:jaxrs-client-1.1-testing") include(":instrumentation:jaxrs-client:jaxrs-client-2.0-testing") +include(":instrumentation:jaxws:jaxws-metro-2.2:javaagent") include(":instrumentation:jaxws:jaxws-2.0:javaagent") include(":instrumentation:jaxws:jaxws-2.0-arquillian-testing") include(":instrumentation:jaxws:jaxws-2.0-axis2-1.6:javaagent") include(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent") include(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent-unit-tests") -include(":instrumentation:jaxws:jaxws-2.0-metro-2.2:javaagent") +include(":instrumentation:jaxws:jaxws-2.0-metro-2.2-testing") include(":instrumentation:jaxws:jaxws-2.0-common-testing") include(":instrumentation:jaxws:jaxws-2.0-tomee-testing") include(":instrumentation:jaxws:jaxws-2.0-wildfly-testing") +include(":instrumentation:jaxws:jaxws-3.0-common-testing") +include(":instrumentation:jaxws:jaxws-3.0-metro-2.2-testing") include(":instrumentation:jaxws:jaxws-common:javaagent") include(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent") include(":instrumentation:jboss-logmanager:jboss-logmanager-appender-1.1:javaagent")