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 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 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 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] |
|
| [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 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] |
|
| [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.xml.ws:jaxws-api:2.0")
|
||||||
api("javax.jws:javax.jws-api:1.1")
|
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.eclipse.jetty:jetty-webapp:9.4.35.v20201120")
|
||||||
api("org.springframework.ws:spring-ws-core:3.0.0.RELEASE")
|
api("org.springframework.ws:spring-ws-core:3.0.0.RELEASE")
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,17 @@
|
||||||
plugins {
|
plugins {
|
||||||
id("otel.javaagent-instrumentation")
|
id("otel.javaagent-testing")
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))
|
testLibrary("com.sun.xml.ws:jaxws-rt:2.2.0.1")
|
||||||
|
|
||||||
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
|
// early versions of streambuffer depend on latest release of org.jvnet.staxex:stax-ex
|
||||||
// which doesn't work with java 8
|
// which doesn't work with java 8
|
||||||
library("com.sun.xml.stream.buffer:streambuffer:1.4")
|
testLibrary("com.sun.xml.stream.buffer:streambuffer:1.4")
|
||||||
|
|
||||||
compileOnly("javax.servlet:javax.servlet-api:3.0.1")
|
|
||||||
|
|
||||||
|
testImplementation("javax.servlet:javax.servlet-api:3.0.1")
|
||||||
testImplementation(project(":instrumentation:jaxws:jaxws-2.0-common-testing"))
|
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-2.0:javaagent"))
|
||||||
testInstrumentation(project(":instrumentation:jaxws:jaxws-jws-api-1.1: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 SCOPE_KEY = MetroHelper.class.getName() + ".Scope";
|
||||||
private static final String THROWABLE_KEY = MetroHelper.class.getName() + ".Throwable";
|
private static final String THROWABLE_KEY = MetroHelper.class.getName() + ".Throwable";
|
||||||
|
|
||||||
|
private static final MetroServerSpanNameUpdater SPAN_NAME_UPDATER =
|
||||||
|
new MetroServerSpanNameUpdater();
|
||||||
|
|
||||||
private MetroHelper() {}
|
private MetroHelper() {}
|
||||||
|
|
||||||
public static void start(WSEndpoint<?> endpoint, Packet packet) {
|
public static void start(WSEndpoint<?> endpoint, Packet packet) {
|
||||||
Context parentContext = Context.current();
|
Context parentContext = Context.current();
|
||||||
|
|
||||||
MetroRequest request = new MetroRequest(endpoint, packet);
|
MetroRequest request = new MetroRequest(endpoint, packet);
|
||||||
MetroServerSpanNaming.updateServerSpanName(parentContext, request);
|
SPAN_NAME_UPDATER.updateServerSpanName(parentContext, request);
|
||||||
|
|
||||||
if (!instrumenter().shouldStart(parentContext, request)) {
|
if (!instrumenter().shouldStart(parentContext, request)) {
|
||||||
return;
|
return;
|
|
@ -22,7 +22,7 @@ public class MetroInstrumentationModule extends InstrumentationModule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
|
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
|
||||||
return hasClassesNamed("javax.jws.WebService");
|
return hasClassesNamed("com.sun.xml.ws.api.pipe.ServerTubeAssemblerContext");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
|
||||||
|
|
||||||
public class MetroSingletons {
|
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;
|
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: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-1.1-testing")
|
||||||
include(":instrumentation:jaxrs-client:jaxrs-client-2.0-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:javaagent")
|
||||||
include(":instrumentation:jaxws:jaxws-2.0-arquillian-testing")
|
include(":instrumentation:jaxws:jaxws-2.0-arquillian-testing")
|
||||||
include(":instrumentation:jaxws:jaxws-2.0-axis2-1.6:javaagent")
|
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")
|
||||||
include(":instrumentation:jaxws:jaxws-2.0-cxf-3.0:javaagent-unit-tests")
|
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-common-testing")
|
||||||
include(":instrumentation:jaxws:jaxws-2.0-tomee-testing")
|
include(":instrumentation:jaxws:jaxws-2.0-tomee-testing")
|
||||||
include(":instrumentation:jaxws:jaxws-2.0-wildfly-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-common:javaagent")
|
||||||
include(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent")
|
include(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent")
|
||||||
include(":instrumentation:jboss-logmanager:jboss-logmanager-appender-1.1:javaagent")
|
include(":instrumentation:jboss-logmanager:jboss-logmanager-appender-1.1:javaagent")
|
||||||
|
|
Loading…
Reference in New Issue