Add instrumentation for jaxws metro 3.0+ (#9705)
This commit is contained in:
parent
71ea355f99
commit
dc975b7bc5
|
@ -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] |
|
||||
|
|
|
@ -14,10 +14,6 @@ dependencies {
|
|||
api("javax.xml.ws:jaxws-api:2.0")
|
||||
api("javax.jws:javax.jws-api:1.1")
|
||||
|
||||
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:9.4.35.v20201120")
|
||||
api("org.springframework.ws:spring-ws-core:3.0.0.RELEASE")
|
||||
|
||||
|
|
|
@ -1,31 +1,17 @@
|
|||
plugins {
|
||||
id("otel.javaagent-instrumentation")
|
||||
}
|
||||
|
||||
muzzle {
|
||||
pass {
|
||||
group.set("com.sun.xml.ws")
|
||||
module.set("jaxws-rt")
|
||||
versions.set("[2.2.0.1,3)")
|
||||
// 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")
|
||||
}
|
||||
id("otel.javaagent-testing")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))
|
||||
|
||||
library("com.sun.xml.ws:jaxws-rt:2.2.0.1")
|
||||
testLibrary("com.sun.xml.ws:jaxws-rt:2.2.0.1")
|
||||
// early versions of streambuffer depend on latest release of org.jvnet.staxex:stax-ex
|
||||
// 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")
|
||||
testLibrary("com.sun.xml.stream.buffer:streambuffer:1.4")
|
||||
|
||||
testImplementation("javax.servlet:javax.servlet-api:3.0.1")
|
||||
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"))
|
||||
|
|
@ -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() {}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
plugins {
|
||||
id("org.unbroken-dome.xjc")
|
||||
id("otel.java-conventions")
|
||||
}
|
||||
|
||||
tasks {
|
||||
named<Checkstyle>("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("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")
|
||||
}
|
|
@ -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<Server> {
|
||||
|
||||
@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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://opentelemetry.io/test/hello-web-service" elementFormDefault="qualified">
|
||||
|
||||
<xs:element name="helloRequest">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="name" type="xs:string" form="unqualified"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="helloResponse">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="message" type="xs:string" form="unqualified"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="hello2Request">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="name" type="xs:string" form="unqualified"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="hello2Response">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="message" type="xs:string" form="unqualified"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
|
@ -0,0 +1,27 @@
|
|||
plugins {
|
||||
id("otel.javaagent-testing")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testLibrary("com.sun.xml.ws:jaxws-rt:3.0.0")
|
||||
|
||||
testImplementation("jakarta.servlet:jakarta.servlet-api:5.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"))
|
||||
}
|
||||
|
||||
otelJava {
|
||||
minJavaVersionSupported.set(JavaVersion.VERSION_17)
|
||||
}
|
||||
|
||||
tasks.withType<Test>().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")
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
class MetroJaxWsTest extends AbstractJaxWsTest {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
|
||||
version="2.0">
|
||||
<endpoint name="Hello" implementation="hello.HelloServiceImpl"
|
||||
url-pattern="/ws/HelloService" />
|
||||
</endpoints>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app version="3.0" metadata-complete="false"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
|
||||
|
||||
<listener>
|
||||
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>WSServlet</servlet-name>
|
||||
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>WSServlet</servlet-name>
|
||||
<url-pattern>/ws/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
|
@ -0,0 +1,26 @@
|
|||
plugins {
|
||||
id("otel.javaagent-instrumentation")
|
||||
}
|
||||
|
||||
muzzle {
|
||||
pass {
|
||||
group.set("com.sun.xml.ws")
|
||||
module.set("jaxws-rt")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))
|
||||
|
||||
library("com.sun.xml.ws:jaxws-rt:2.2.0.1")
|
||||
// early versions of streambuffer depend on latest release of org.jvnet.staxex:stax-ex
|
||||
// which doesn't work with java 8
|
||||
library("com.sun.xml.stream.buffer:streambuffer:1.4")
|
||||
|
||||
compileOnly("javax.xml.ws:jaxws-api:2.0")
|
||||
compileOnly("jakarta.xml.ws:jakarta.xml.ws-api:3.0.0")
|
||||
}
|
|
@ -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;
|
|
@ -22,7 +22,7 @@ public class MetroInstrumentationModule extends InstrumentationModule {
|
|||
|
||||
@Override
|
||||
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
|
||||
return hasClassesNamed("javax.jws.WebService");
|
||||
return hasClassesNamed("com.sun.xml.ws.api.pipe.ServerTubeAssemblerContext");
|
||||
}
|
||||
|
||||
@Override
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* <p>This map will contain at most two entries:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@value javax.xml.ws.handler.MessageContext#SERVLET_REQUEST} to an {@link
|
||||
* HttpServletRequestAdapter} that handles {@code javax.servlet.http.HttpServletRequest}
|
||||
* <li>{@value jakarta.xml.ws.handler.MessageContext#SERVLET_REQUEST} to an {@link
|
||||
* HttpServletRequestAdapter} that handles {@code jakarta.servlet.http.HttpServletRequest}
|
||||
* </ul>
|
||||
*/
|
||||
private final Map<String, HttpServletRequestAdapter> 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) {
|
||||
HttpServletRequestAdapter adapter;
|
||||
try {
|
||||
adapter = new HttpServletRequestAdapter(Class.forName(httpServletRequestClassName));
|
||||
} 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<String, HttpServletRequestAdapter> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
|||
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<MetroRequest, Void> INSTRUMENTER;
|
||||
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue