Instrument jsf action calls (#2018)
This commit is contained in:
parent
c6cc263c6e
commit
9825ab0afa
|
|
@ -10,6 +10,7 @@ import static io.opentelemetry.api.trace.Span.Kind.INTERNAL
|
|||
import static io.opentelemetry.api.trace.Span.Kind.SERVER
|
||||
|
||||
import io.opentelemetry.instrumentation.test.AgentTestRunner
|
||||
import io.opentelemetry.instrumentation.test.RetryOnAddressAlreadyInUseTrait
|
||||
import io.opentelemetry.instrumentation.test.utils.PortUtils
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||
import org.apache.camel.CamelContext
|
||||
|
|
@ -18,7 +19,7 @@ import org.springframework.boot.SpringApplication
|
|||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import spock.lang.Shared
|
||||
|
||||
class RestCamelTest extends AgentTestRunner {
|
||||
class RestCamelTest extends AgentTestRunner implements RetryOnAddressAlreadyInUseTrait {
|
||||
|
||||
@Shared
|
||||
ConfigurableApplicationContext server
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package test
|
|||
import static io.opentelemetry.api.trace.Span.Kind.SERVER
|
||||
|
||||
import io.opentelemetry.instrumentation.test.AgentTestRunner
|
||||
import io.opentelemetry.instrumentation.test.RetryOnAddressAlreadyInUseTrait
|
||||
import io.opentelemetry.instrumentation.test.utils.OkHttpUtils
|
||||
import io.opentelemetry.instrumentation.test.utils.PortUtils
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||
|
|
@ -19,7 +20,7 @@ import org.springframework.boot.SpringApplication
|
|||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import spock.lang.Shared
|
||||
|
||||
class SingleServiceCamelTest extends AgentTestRunner {
|
||||
class SingleServiceCamelTest extends AgentTestRunner implements RetryOnAddressAlreadyInUseTrait {
|
||||
|
||||
@Shared
|
||||
ConfigurableApplicationContext server
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import static io.opentelemetry.api.trace.Span.Kind.INTERNAL
|
|||
import static io.opentelemetry.api.trace.Span.Kind.SERVER
|
||||
|
||||
import io.opentelemetry.instrumentation.test.AgentTestRunner
|
||||
import io.opentelemetry.instrumentation.test.RetryOnAddressAlreadyInUseTrait
|
||||
import io.opentelemetry.instrumentation.test.utils.PortUtils
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||
import org.apache.camel.CamelContext
|
||||
|
|
@ -20,7 +21,7 @@ import org.springframework.boot.SpringApplication
|
|||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import spock.lang.Shared
|
||||
|
||||
class TwoServicesWithDirectClientCamelTest extends AgentTestRunner {
|
||||
class TwoServicesWithDirectClientCamelTest extends AgentTestRunner implements RetryOnAddressAlreadyInUseTrait {
|
||||
|
||||
@Shared
|
||||
int portOne
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import static Jms2Test.producerSpan
|
|||
|
||||
import com.google.common.io.Files
|
||||
import io.opentelemetry.instrumentation.test.AgentTestRunner
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.jms.Session
|
||||
|
|
@ -101,7 +102,9 @@ class SpringTemplateJms2Test extends AgentTestRunner {
|
|||
def "send and receive message generates spans"() {
|
||||
setup:
|
||||
AtomicReference<String> msgId = new AtomicReference<>()
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1)
|
||||
Thread.start {
|
||||
countDownLatch.countDown()
|
||||
TextMessage msg = template.receive(destination)
|
||||
assert msg.text == messageText
|
||||
msgId.set(msg.getJMSMessageID())
|
||||
|
|
@ -111,6 +114,8 @@ class SpringTemplateJms2Test extends AgentTestRunner {
|
|||
session -> template.getMessageConverter().toMessage("responded!", session)
|
||||
}
|
||||
}
|
||||
// wait for thread to start, we expect the first span to be from receive
|
||||
countDownLatch.await()
|
||||
TextMessage receivedMessage = template.sendAndReceive(destination) {
|
||||
session -> template.getMessageConverter().toMessage(messageText, session)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
apply from: "$rootDir/gradle/instrumentation-library.gradle"
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'jakarta.faces', name: 'jakarta.faces-api', version: '2.3.2'
|
||||
compileOnly group: 'jakarta.el', name: 'jakarta.el-api', version: '3.0.3'
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.jsf;
|
||||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
|
||||
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.component.ActionSource2;
|
||||
import javax.faces.component.UIViewRoot;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.event.ActionEvent;
|
||||
|
||||
public abstract class JsfTracer extends BaseTracer {
|
||||
|
||||
public Span startSpan(ActionEvent event) {
|
||||
// https://jakarta.ee/specifications/faces/2.3/apidocs/index.html?javax/faces/component/ActionSource2.html
|
||||
// ActionSource2 was added in JSF 1.2 and is implemented by components that have an action
|
||||
// attribute such as a button or a link
|
||||
if (event.getComponent() instanceof ActionSource2) {
|
||||
ActionSource2 actionSource = (ActionSource2) event.getComponent();
|
||||
if (actionSource.getActionExpression() != null) {
|
||||
// either an el expression in the form #{bean.method()} or navigation case name
|
||||
String expressionString = actionSource.getActionExpression().getExpressionString();
|
||||
// start span only if expression string is really an expression
|
||||
if (expressionString.startsWith("#{") || expressionString.startsWith("${")) {
|
||||
return tracer.spanBuilder(expressionString).startSpan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void updateServerSpanName(Context context, FacesContext facesContext) {
|
||||
Span serverSpan = getCurrentServerSpan();
|
||||
if (serverSpan == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIViewRoot uiViewRoot = facesContext.getViewRoot();
|
||||
if (uiViewRoot == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// JSF spec 7.6.2
|
||||
// view id is a context relative path to the web application resource that produces the view,
|
||||
// such as a JSP page or a Facelets page.
|
||||
String viewId = uiViewRoot.getViewId();
|
||||
serverSpan.updateName(ServletContextPath.prepend(context, viewId));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Throwable unwrapThrowable(Throwable throwable) {
|
||||
while (throwable.getCause() != null && throwable instanceof FacesException) {
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
|
||||
return super.unwrapThrowable(throwable);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
apply from: "$rootDir/gradle/java.gradle"
|
||||
|
||||
dependencies {
|
||||
api deps.testLogging
|
||||
|
||||
compileOnly group: 'jakarta.faces', name: 'jakarta.faces-api', version: '2.3.2'
|
||||
compileOnly group: 'jakarta.el', name: 'jakarta.el-api', version: '3.0.3'
|
||||
|
||||
implementation(project(':testing-common')) {
|
||||
exclude group: 'org.eclipse.jetty', module: 'jetty-server'
|
||||
}
|
||||
implementation group: 'org.jsoup', name: 'jsoup', version: '1.13.1'
|
||||
|
||||
def jettyVersion = '9.4.35.v20201120'
|
||||
api group: 'org.eclipse.jetty', name: 'jetty-annotations', version: jettyVersion
|
||||
implementation group: 'org.eclipse.jetty', name: 'apache-jsp', version: jettyVersion
|
||||
implementation group: 'org.glassfish', name: 'jakarta.el', version: '3.0.2'
|
||||
implementation group: 'jakarta.websocket', name: 'jakarta.websocket-api', version: '1.1.1'
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import static io.opentelemetry.api.trace.Span.Kind.INTERNAL
|
||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan
|
||||
|
||||
import io.opentelemetry.instrumentation.test.AgentTestRunner
|
||||
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTestTrait
|
||||
import io.opentelemetry.sdk.trace.data.SpanData
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.Response
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration
|
||||
import org.eclipse.jetty.server.Server
|
||||
import org.eclipse.jetty.util.resource.Resource
|
||||
import org.eclipse.jetty.webapp.WebAppContext
|
||||
import org.jsoup.Jsoup
|
||||
import spock.lang.Unroll
|
||||
|
||||
abstract class BaseJsfTest extends AgentTestRunner implements HttpServerTestTrait<Server> {
|
||||
|
||||
@Override
|
||||
Server startServer(int port) {
|
||||
String jsfVersion = getJsfVersion()
|
||||
|
||||
List<String> configurationClasses = new ArrayList<>()
|
||||
Collections.addAll(configurationClasses, WebAppContext.getDefaultConfigurationClasses())
|
||||
configurationClasses.add(AnnotationConfiguration.getName())
|
||||
|
||||
WebAppContext webAppContext = new WebAppContext()
|
||||
webAppContext.setContextPath(getContextPath())
|
||||
webAppContext.setConfigurationClasses(configurationClasses)
|
||||
// set up test application
|
||||
webAppContext.setBaseResource(Resource.newSystemResource("test-app-" + jsfVersion))
|
||||
// add additional resources for test app
|
||||
Resource extraResource = Resource.newSystemResource("test-app-" + jsfVersion + "-extra")
|
||||
if (extraResource != null) {
|
||||
webAppContext.getMetaData().addWebInfJar(extraResource)
|
||||
}
|
||||
webAppContext.getMetaData().getWebInfClassesDirs().add(Resource.newClassPathResource("/"))
|
||||
|
||||
def jettyServer = new Server(port)
|
||||
jettyServer.connectors.each {
|
||||
it.setHost('localhost')
|
||||
}
|
||||
|
||||
jettyServer.setHandler(webAppContext)
|
||||
jettyServer.start()
|
||||
|
||||
return jettyServer
|
||||
}
|
||||
|
||||
abstract String getJsfVersion();
|
||||
|
||||
@Override
|
||||
void stopServer(Server server) {
|
||||
server.stop()
|
||||
server.destroy()
|
||||
}
|
||||
|
||||
@Override
|
||||
String getContextPath() {
|
||||
return "/jetty-context"
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "test #path"() {
|
||||
setup:
|
||||
def url = HttpUrl.get(address.resolve("hello.jsf")).newBuilder().build()
|
||||
def request = request(url, "GET", null).build()
|
||||
Response response = client.newCall(request).execute()
|
||||
|
||||
expect:
|
||||
response.code() == 200
|
||||
response.body().string().trim() == "Hello"
|
||||
|
||||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
basicSpan(it, 0, getContextPath() + "/hello.xhtml", null)
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
path << ['hello.jsf', 'faces/hello.xhtml']
|
||||
}
|
||||
|
||||
def "test greeting"() {
|
||||
// we need to display the page first before posting data to it
|
||||
setup:
|
||||
def url = HttpUrl.get(address.resolve("greeting.jsf")).newBuilder().build()
|
||||
def request = request(url, "GET", null).build()
|
||||
Response response = client.newCall(request).execute()
|
||||
def doc = Jsoup.parse(response.body().string())
|
||||
|
||||
expect:
|
||||
response.code() == 200
|
||||
doc.selectFirst("title").text() == "Hello, World!"
|
||||
|
||||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
basicSpan(it, 0, getContextPath() + "/greeting.xhtml", null)
|
||||
}
|
||||
}
|
||||
TEST_WRITER.clear()
|
||||
|
||||
when:
|
||||
// extract parameters needed to post back form
|
||||
def viewState = doc.selectFirst("[name=javax.faces.ViewState]")?.val()
|
||||
def formAction = doc.selectFirst("#app-form").attr("action")
|
||||
def jsessionid = formAction.substring(formAction.indexOf("jsessionid=") + "jsessionid=".length())
|
||||
|
||||
then:
|
||||
viewState != null
|
||||
jsessionid != null
|
||||
|
||||
when:
|
||||
// use the session created for first request
|
||||
def url2 = HttpUrl.get(address.resolve("greeting.jsf;jsessionid=" + jsessionid)).newBuilder().build()
|
||||
// set up form parameter for post
|
||||
RequestBody formBody = new FormBody.Builder()
|
||||
.add("app-form", "app-form")
|
||||
// value used for name is returned in app-form:output-message element
|
||||
.add("app-form:name", "test")
|
||||
.add("app-form:submit", "Say hello")
|
||||
.add("app-form_SUBMIT", "1") // MyFaces
|
||||
.add("javax.faces.ViewState", viewState)
|
||||
.build()
|
||||
def request2 = this.request(url2, "POST", formBody).build()
|
||||
Response response2 = client.newCall(request2).execute()
|
||||
def responseContent = response2.body().string()
|
||||
def doc2 = Jsoup.parse(responseContent)
|
||||
|
||||
then:
|
||||
response2.code() == 200
|
||||
doc2.getElementById("app-form:output-message").text() == "Hello test"
|
||||
|
||||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
basicSpan(it, 0, getContextPath() + "/greeting.xhtml", null)
|
||||
handlerSpan(it, 1, span(0), "#{greetingForm.submit()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test exception"() {
|
||||
// we need to display the page first before posting data to it
|
||||
setup:
|
||||
def url = HttpUrl.get(address.resolve("greeting.jsf")).newBuilder().build()
|
||||
def request = request(url, "GET", null).build()
|
||||
Response response = client.newCall(request).execute()
|
||||
def doc = Jsoup.parse(response.body().string())
|
||||
|
||||
expect:
|
||||
response.code() == 200
|
||||
doc.selectFirst("title").text() == "Hello, World!"
|
||||
|
||||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
basicSpan(it, 0, getContextPath() + "/greeting.xhtml", null)
|
||||
}
|
||||
}
|
||||
TEST_WRITER.clear()
|
||||
|
||||
when:
|
||||
// extract parameters needed to post back form
|
||||
def viewState = doc.selectFirst("[name=javax.faces.ViewState]").val()
|
||||
def formAction = doc.selectFirst("#app-form").attr("action")
|
||||
def jsessionid = formAction.substring(formAction.indexOf("jsessionid=") + "jsessionid=".length())
|
||||
|
||||
then:
|
||||
viewState != null
|
||||
jsessionid != null
|
||||
|
||||
when:
|
||||
// use the session created for first request
|
||||
def url2 = HttpUrl.get(address.resolve("greeting.jsf;jsessionid=" + jsessionid)).newBuilder().build()
|
||||
// set up form parameter for post
|
||||
RequestBody formBody = new FormBody.Builder()
|
||||
.add("app-form", "app-form")
|
||||
// setting name parameter to "exception" triggers throwing exception in GreetingForm
|
||||
.add("app-form:name", "exception")
|
||||
.add("app-form:submit", "Say hello")
|
||||
.add("app-form_SUBMIT", "1") // MyFaces
|
||||
.add("javax.faces.ViewState", viewState)
|
||||
.build()
|
||||
def request2 = this.request(url2, "POST", formBody).build()
|
||||
Response response2 = client.newCall(request2).execute()
|
||||
|
||||
then:
|
||||
response2.code() == 500
|
||||
|
||||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
basicSpan(it, 0, getContextPath() + "/greeting.xhtml", null, new Exception("submit exception"))
|
||||
handlerSpan(it, 1, span(0), "#{greetingForm.submit()}", new Exception("submit exception"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Request.Builder request(HttpUrl url, String method, RequestBody body) {
|
||||
return new Request.Builder()
|
||||
.url(url)
|
||||
.method(method, body)
|
||||
.header("User-Agent", TEST_USER_AGENT)
|
||||
.header("X-Forwarded-For", TEST_CLIENT_IP)
|
||||
}
|
||||
|
||||
void handlerSpan(TraceAssert trace, int index, Object parent, String spanName, Exception expectedException = null) {
|
||||
trace.span(index) {
|
||||
name spanName
|
||||
kind INTERNAL
|
||||
errored expectedException != null
|
||||
if (expectedException != null) {
|
||||
errorEvent(expectedException.getClass(), expectedException.getMessage())
|
||||
}
|
||||
childOf((SpanData) parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import javax.servlet.Filter
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.FilterConfig
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.ServletRequest
|
||||
import javax.servlet.ServletResponse
|
||||
|
||||
class ExceptionFilter implements Filter {
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
try {
|
||||
chain.doFilter(request, response)
|
||||
} catch (Exception exception) {
|
||||
// to ease testing unwrap our exception to root cause
|
||||
Exception tmp = exception
|
||||
while (tmp.getCause() != null) {
|
||||
tmp = tmp.getCause()
|
||||
}
|
||||
if (tmp.getMessage() != null && tmp.getMessage().contains("submit exception")) {
|
||||
throw tmp
|
||||
}
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
class GreetingForm {
|
||||
|
||||
String name = ""
|
||||
String message = ""
|
||||
|
||||
String getName() {
|
||||
name
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
this.name = name
|
||||
}
|
||||
|
||||
String getMessage() {
|
||||
return message
|
||||
}
|
||||
|
||||
void submit() {
|
||||
message = "Hello " + name
|
||||
if (name == "exception") {
|
||||
throw new Exception("submit exception")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
|
||||
version="1.2">
|
||||
<managed-bean>
|
||||
<managed-bean-name>greetingForm</managed-bean-name>
|
||||
<managed-bean-class>GreetingForm</managed-bean-class>
|
||||
<managed-bean-scope>request</managed-bean-scope>
|
||||
</managed-bean>
|
||||
|
||||
<application>
|
||||
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
|
||||
</application>
|
||||
</faces-config>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?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">
|
||||
|
||||
<servlet>
|
||||
<servlet-name>Faces Servlet</servlet-name>
|
||||
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Faces Servlet</servlet-name>
|
||||
<url-pattern>*.jsf</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Faces Servlet</servlet-name>
|
||||
<url-pattern>/faces/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<filter>
|
||||
<filter-name>ExceptionFilter</filter-name>
|
||||
<filter-class>ExceptionFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>ExceptionFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<context-param>
|
||||
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
|
||||
<param-value>.xhtml</param-value>
|
||||
</context-param>
|
||||
|
||||
</web-app>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://java.sun.com/jsf/html">
|
||||
<head>
|
||||
<title>Hello, World!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h:form id="app-form">
|
||||
<p>
|
||||
<h:outputLabel for="name" value="Enter your name" required="true"/>
|
||||
<h:inputText id="name" value="#{greetingForm.name}"/>
|
||||
<h:message for="name"/>
|
||||
</p>
|
||||
<p>
|
||||
<h:commandButton id="submit" value="Say hello" action="#{greetingForm.submit()}">
|
||||
</h:commandButton>
|
||||
</p>
|
||||
<p>
|
||||
<h:outputText id="output-message" value="#{greetingForm.message}"/>
|
||||
</p>
|
||||
</h:form>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<f:view xmlns:f="http://java.sun.com/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets">
|
||||
<ui:composition>
|
||||
Hello
|
||||
</ui:composition>
|
||||
</f:view>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" version="2.0">
|
||||
<managed-bean>
|
||||
<managed-bean-name>greetingForm</managed-bean-name>
|
||||
<managed-bean-class>GreetingForm</managed-bean-class>
|
||||
<managed-bean-scope>request</managed-bean-scope>
|
||||
</managed-bean>
|
||||
</faces-config>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?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">
|
||||
|
||||
<filter>
|
||||
<filter-name>ExceptionFilter</filter-name>
|
||||
<filter-class>ExceptionFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>ExceptionFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
</web-app>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html">
|
||||
<h:head>
|
||||
<title>Hello, World!</title>
|
||||
</h:head>
|
||||
<h:body>
|
||||
<h:form id="app-form">
|
||||
<p>
|
||||
<h:outputLabel for="name" value="Enter your name" required="true"/>
|
||||
<h:inputText id="name" value="#{greetingForm.name}"/>
|
||||
<h:message for="name"/>
|
||||
</p>
|
||||
<p>
|
||||
<h:commandButton id="submit" value="Say hello" action="#{greetingForm.submit()}"/>
|
||||
</p>
|
||||
<p>
|
||||
<h:outputText id="output-message" value="#{greetingForm.message}"/>
|
||||
</p>
|
||||
</h:form>
|
||||
</h:body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<f:view xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
contentType="text/html"
|
||||
encoding="UTF-8">
|
||||
<ui:composition>
|
||||
Hello
|
||||
</ui:composition>
|
||||
</f:view>
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
muzzle {
|
||||
pass {
|
||||
group = "org.glassfish"
|
||||
module = "jakarta.faces"
|
||||
versions = "[2.3.9,3)"
|
||||
extraDependency "javax.el:el-api:2.2"
|
||||
}
|
||||
pass {
|
||||
group = "org.glassfish"
|
||||
module = "javax.faces"
|
||||
versions = "[2.0.7,3)"
|
||||
extraDependency "javax.el:el-api:2.2"
|
||||
}
|
||||
pass {
|
||||
group = "com.sun.faces"
|
||||
module = "jsf-impl"
|
||||
versions = "[2.1,2.2)"
|
||||
extraDependency "javax.faces:jsf-api:2.1"
|
||||
extraDependency "javax.el:el-api:1.0"
|
||||
}
|
||||
pass {
|
||||
group = "com.sun.faces"
|
||||
module = "jsf-impl"
|
||||
versions = "[2.0,2.1)"
|
||||
extraDependency "javax.faces:jsf-api:2.0"
|
||||
extraDependency "javax.el:el-api:1.0"
|
||||
}
|
||||
pass {
|
||||
group = "javax.faces"
|
||||
module = "jsf-impl"
|
||||
versions = "[1.2,2)"
|
||||
extraDependency "javax.faces:jsf-api:1.2"
|
||||
extraDependency "javax.el:el-api:1.0"
|
||||
}
|
||||
fail {
|
||||
group = "org.glassfish"
|
||||
module = "jakarta.faces"
|
||||
versions = "[3.0,)"
|
||||
extraDependency "javax.el:el-api:2.2"
|
||||
}
|
||||
fail {
|
||||
group = "javax.faces"
|
||||
module = "jsf-impl"
|
||||
versions = "[1.1,1.2)"
|
||||
extraDependency "javax.faces:jsf-api:1.1_02"
|
||||
extraDependency "javax.el:el-api:1.0"
|
||||
}
|
||||
}
|
||||
|
||||
testSets {
|
||||
mojarra12Test
|
||||
mojarra2Test
|
||||
latestDepTest {
|
||||
extendsFrom mojarra2Test
|
||||
dirName = 'mojarra2LatestTest'
|
||||
}
|
||||
}
|
||||
|
||||
test.dependsOn mojarra12Test, mojarra2Test
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'javax.faces', name: 'jsf-api', version: '1.2'
|
||||
|
||||
implementation project(':instrumentation:jsf:jsf-common:library')
|
||||
|
||||
testImplementation project(':instrumentation:jsf:jsf-testing-common')
|
||||
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
|
||||
|
||||
mojarra12TestImplementation group: 'javax.faces', name: 'jsf-impl', version: '1.2-20'
|
||||
mojarra12TestImplementation group: 'javax.faces', name: 'jsf-api', version: '1.2'
|
||||
mojarra12TestImplementation group: 'com.sun.facelets', name: 'jsf-facelets', version: '1.1.14'
|
||||
|
||||
mojarra2TestImplementation group: 'org.glassfish', name: 'jakarta.faces', version: '2.3.12'
|
||||
|
||||
latestDepTestImplementation group: 'org.glassfish', name: 'jakarta.faces', version: '2+'
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mojarra;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.mojarra.MojarraTracer.tracer;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.Map;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class ActionListenerImplInstrumentation implements TypeInstrumentation {
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("com.sun.faces.application.ActionListenerImpl");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("processAction"),
|
||||
ActionListenerImplInstrumentation.class.getName() + "$ProcessActionAdvice");
|
||||
}
|
||||
|
||||
public static class ProcessActionAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void onEnter(
|
||||
@Advice.Argument(0) ActionEvent event,
|
||||
@Advice.Local("otelSpan") Span span,
|
||||
@Advice.Local("otelScope") Scope scope) {
|
||||
span = tracer().startSpan(event);
|
||||
if (span != null) {
|
||||
scope = tracer().startScope(span);
|
||||
}
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Thrown Throwable throwable,
|
||||
@Advice.Local("otelSpan") Span span,
|
||||
@Advice.Local("otelScope") Scope scope) {
|
||||
if (scope != null) {
|
||||
scope.close();
|
||||
if (throwable != null) {
|
||||
tracer().endExceptionally(span, throwable);
|
||||
} else {
|
||||
tracer().end(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mojarra;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.List;
|
||||
|
||||
@AutoService(InstrumentationModule.class)
|
||||
public class MojarraInstrumentationModule extends InstrumentationModule {
|
||||
public MojarraInstrumentationModule() {
|
||||
super("mojarra", "mojarra-1.2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeInstrumentation> typeInstrumentations() {
|
||||
return asList(new ActionListenerImplInstrumentation(), new RestoreViewPhaseInstrumentation());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mojarra;
|
||||
|
||||
import io.opentelemetry.instrumentation.jsf.JsfTracer;
|
||||
|
||||
public class MojarraTracer extends JsfTracer {
|
||||
private static final MojarraTracer TRACER = new MojarraTracer();
|
||||
|
||||
public static MojarraTracer tracer() {
|
||||
return TRACER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInstrumentationName() {
|
||||
return "io.opentelemetry.javaagent.mojarra";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mojarra;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.mojarra.MojarraTracer.tracer;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.Map;
|
||||
import javax.faces.context.FacesContext;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class RestoreViewPhaseInstrumentation implements TypeInstrumentation {
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("com.sun.faces.lifecycle.RestoreViewPhase");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("execute").and(takesArgument(0, named("javax.faces.context.FacesContext"))),
|
||||
RestoreViewPhaseInstrumentation.class.getName() + "$ExecuteAdvice");
|
||||
}
|
||||
|
||||
public static class ExecuteAdvice {
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void onExit(@Advice.Argument(0) FacesContext facesContext) {
|
||||
tracer().updateServerSpanName(Java8BytecodeBridge.currentContext(), facesContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
class Mojarra12Test extends BaseJsfTest {
|
||||
@Override
|
||||
String getJsfVersion() {
|
||||
"1.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">
|
||||
|
||||
<listener>
|
||||
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
|
||||
</listener>
|
||||
|
||||
</web-fragment>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
class Mojarra2LatestTest extends BaseJsfTest {
|
||||
@Override
|
||||
String getJsfVersion() {
|
||||
"2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
class Mojarra2Test extends BaseJsfTest {
|
||||
@Override
|
||||
String getJsfVersion() {
|
||||
"2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
muzzle {
|
||||
pass {
|
||||
group = "org.apache.myfaces.core"
|
||||
module = "myfaces-impl"
|
||||
versions = "[1.2,3)"
|
||||
extraDependency "jakarta.el:jakarta.el-api:3.0.3"
|
||||
assertInverse = true
|
||||
}
|
||||
}
|
||||
|
||||
testSets {
|
||||
myfaces12Test
|
||||
myfaces2Test
|
||||
latestDepTest {
|
||||
extendsFrom myfaces2Test
|
||||
dirName = 'myfaces2LatestTest'
|
||||
}
|
||||
}
|
||||
|
||||
test.dependsOn myfaces12Test, myfaces2Test
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'org.apache.myfaces.core', name: 'myfaces-api', version: '1.2.12'
|
||||
compileOnly group: 'javax.el', name: 'el-api', version: '1.0'
|
||||
|
||||
implementation project(':instrumentation:jsf:jsf-common:library')
|
||||
|
||||
testImplementation project(':instrumentation:jsf:jsf-testing-common')
|
||||
testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
|
||||
|
||||
myfaces12TestImplementation group: 'org.apache.myfaces.core', name: 'myfaces-impl', version: '1.2.12'
|
||||
myfaces12TestImplementation group: 'com.sun.facelets', name: 'jsf-facelets', version: '1.1.14'
|
||||
|
||||
myfaces2TestImplementation group: 'org.apache.myfaces.core', name: 'myfaces-impl', version: '2.3.2'
|
||||
myfaces2TestImplementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.11'
|
||||
myfaces2TestImplementation group: 'com.sun.xml.bind', name: 'jaxb-impl', version: '2.2.11'
|
||||
|
||||
latestDepTestImplementation group: 'org.apache.myfaces.core', name: 'myfaces-impl', version: '2+'
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.myfaces;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.myfaces.MyFacesTracer.tracer;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.Map;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class ActionListenerImplInstrumentation implements TypeInstrumentation {
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("org.apache.myfaces.application.ActionListenerImpl");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("processAction"),
|
||||
ActionListenerImplInstrumentation.class.getName() + "$ProcessActionAdvice");
|
||||
}
|
||||
|
||||
public static class ProcessActionAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void onEnter(
|
||||
@Advice.Argument(0) ActionEvent event,
|
||||
@Advice.Local("otelSpan") Span span,
|
||||
@Advice.Local("otelScope") Scope scope) {
|
||||
span = tracer().startSpan(event);
|
||||
if (span != null) {
|
||||
scope = tracer().startScope(span);
|
||||
}
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void stopSpan(
|
||||
@Advice.Thrown Throwable throwable,
|
||||
@Advice.Local("otelSpan") Span span,
|
||||
@Advice.Local("otelScope") Scope scope) {
|
||||
if (scope != null) {
|
||||
scope.close();
|
||||
if (throwable != null) {
|
||||
tracer().endExceptionally(span, throwable);
|
||||
} else {
|
||||
tracer().end(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.myfaces;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.List;
|
||||
|
||||
@AutoService(InstrumentationModule.class)
|
||||
public class MyFacesInstrumentationModule extends InstrumentationModule {
|
||||
public MyFacesInstrumentationModule() {
|
||||
super("myfaces", "myfaces-1.2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeInstrumentation> typeInstrumentations() {
|
||||
return asList(
|
||||
new ActionListenerImplInstrumentation(), new RestoreViewExecutorInstrumentation());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.myfaces;
|
||||
|
||||
import io.opentelemetry.instrumentation.jsf.JsfTracer;
|
||||
import javax.el.ELException;
|
||||
|
||||
public class MyFacesTracer extends JsfTracer {
|
||||
private static final MyFacesTracer TRACER = new MyFacesTracer();
|
||||
|
||||
public static MyFacesTracer tracer() {
|
||||
return TRACER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Throwable unwrapThrowable(Throwable throwable) {
|
||||
throwable = super.unwrapThrowable(throwable);
|
||||
|
||||
while (throwable instanceof ELException) {
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
|
||||
return throwable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInstrumentationName() {
|
||||
return "io.opentelemetry.javaagent.myfaces";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.myfaces;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.myfaces.MyFacesTracer.tracer;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.Map;
|
||||
import javax.faces.context.FacesContext;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class RestoreViewExecutorInstrumentation implements TypeInstrumentation {
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("org.apache.myfaces.lifecycle.RestoreViewExecutor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
named("execute").and(takesArgument(0, named("javax.faces.context.FacesContext"))),
|
||||
RestoreViewExecutorInstrumentation.class.getName() + "$ExecuteAdvice");
|
||||
}
|
||||
|
||||
public static class ExecuteAdvice {
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void onExit(@Advice.Argument(0) FacesContext facesContext) {
|
||||
tracer().updateServerSpanName(Java8BytecodeBridge.currentContext(), facesContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
class Myfaces12Test extends BaseJsfTest {
|
||||
@Override
|
||||
String getJsfVersion() {
|
||||
"1.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">
|
||||
|
||||
<!-- without this response status is 200 even when there is an exception -->
|
||||
<context-param>
|
||||
<param-name>org.apache.myfaces.ERROR_HANDLING</param-name>
|
||||
<param-value>false</param-value>
|
||||
</context-param>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
|
||||
</listener>
|
||||
|
||||
</web-fragment>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
class Myfaces2LatestTest extends BaseJsfTest {
|
||||
@Override
|
||||
String getJsfVersion() {
|
||||
"2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">
|
||||
|
||||
<!-- need to explicitly add ServletContextInitializer as myfaces jars are not inside webapp -->
|
||||
<listener>
|
||||
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
|
||||
</listener>
|
||||
|
||||
</web-fragment>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
class Myfaces2Test extends BaseJsfTest {
|
||||
@Override
|
||||
String getJsfVersion() {
|
||||
"2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">
|
||||
|
||||
<!-- need to explicitly add ServletContextInitializer as myfaces jars are not inside webapp -->
|
||||
<listener>
|
||||
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
|
||||
</listener>
|
||||
|
||||
</web-fragment>
|
||||
|
|
@ -23,12 +23,12 @@ public class ServletAndFilterInstrumentation implements TypeInstrumentation {
|
|||
|
||||
@Override
|
||||
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||
return hasClassesNamed("javax.servlet.http.HttpServlet");
|
||||
return hasClassesNamed("javax.servlet.Servlet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return safeHasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.http.HttpServlet"));
|
||||
return safeHasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.Servlet"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class ServletAndFilterInstrumentation implements TypeInstrumentation {
|
|||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return safeHasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.http.HttpServlet"));
|
||||
return safeHasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.Servlet"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -143,7 +143,8 @@ public class GlobalIgnoresMatcher<T extends TypeDescription>
|
|||
if (name.startsWith("com.sun.")) {
|
||||
if (name.startsWith("com.sun.messaging.")
|
||||
|| name.startsWith("com.sun.jersey.api.client")
|
||||
|| name.startsWith("com.sun.appserv")) {
|
||||
|| name.startsWith("com.sun.appserv")
|
||||
|| name.startsWith("com.sun.faces")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,10 @@ include ':instrumentation:jedis:jedis-1.4:javaagent'
|
|||
include ':instrumentation:jedis:jedis-3.0:javaagent'
|
||||
include ':instrumentation:jetty-8.0:javaagent'
|
||||
include ':instrumentation:jms-1.1:javaagent'
|
||||
include ':instrumentation:jsf:jsf-common:library'
|
||||
include ':instrumentation:jsf:jsf-testing-common'
|
||||
include ':instrumentation:jsf:mojarra-1.2:javaagent'
|
||||
include ':instrumentation:jsf:myfaces-1.2:javaagent'
|
||||
include ':instrumentation:jsp-2.3:javaagent'
|
||||
include ':instrumentation:kafka-clients-0.11:javaagent'
|
||||
include ':instrumentation:kafka-streams-0.11:javaagent'
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ import spock.lang.Specification;
|
|||
@SpecMetadata(filename = "AgentTestRunner.java", line = 0)
|
||||
public abstract class AgentTestRunner extends Specification {
|
||||
|
||||
private static final org.slf4j.Logger log = LoggerFactory.getLogger(AgentTestRunner.class);
|
||||
|
||||
static {
|
||||
// always run with the thread propagation debugger to help track down sporadic test failures
|
||||
System.setProperty("otel.threadPropagationDebugger", "true");
|
||||
|
|
@ -102,31 +100,6 @@ public abstract class AgentTestRunner extends Specification {
|
|||
AgentTestingExporterAccess.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used by setupSpec() methods to auto-retry setup that depends on finding and then using
|
||||
* an available free port, because that kind of setup can fail sporadically if the available port
|
||||
* gets re-used between when we find the available port and when we use it.
|
||||
*
|
||||
* @param closure the groovy closure to run with retry
|
||||
*/
|
||||
public static void withRetryOnAddressAlreadyInUse(Closure<?> closure) {
|
||||
withRetryOnAddressAlreadyInUse(closure, 3);
|
||||
}
|
||||
|
||||
private static void withRetryOnAddressAlreadyInUse(Closure<?> closure, int numRetries) {
|
||||
try {
|
||||
closure.call();
|
||||
} catch (Throwable t) {
|
||||
// typically this is "java.net.BindException: Address already in use", but also can be
|
||||
// "io.netty.channel.unix.Errors$NativeIoException: bind() failed: Address already in use"
|
||||
if (numRetries == 0 || !t.getMessage().contains("Address already in use")) {
|
||||
throw t;
|
||||
}
|
||||
log.debug("retrying due to bind exception: {}", t.getMessage(), t);
|
||||
withRetryOnAddressAlreadyInUse(closure, numRetries - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static synchronized void agentCleanup() {
|
||||
// Cleanup before assertion.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.test
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
/**
|
||||
* A trait for retrying operation when it fails with "java.net.BindException: Address already in use"
|
||||
*/
|
||||
trait RetryOnAddressAlreadyInUseTrait {
|
||||
private static final Logger log = LoggerFactory.getLogger(RetryOnAddressAlreadyInUseTrait)
|
||||
|
||||
/**
|
||||
* This is used by setupSpec() methods to auto-retry setup that depends on finding and then using
|
||||
* an available free port, because that kind of setup can fail sporadically if the available port
|
||||
* gets re-used between when we find the available port and when we use it.
|
||||
*
|
||||
* @param closure the groovy closure to run with retry
|
||||
*/
|
||||
static void withRetryOnAddressAlreadyInUse(Closure<?> closure) {
|
||||
withRetryOnAddressAlreadyInUse(closure, 3)
|
||||
}
|
||||
|
||||
static void withRetryOnAddressAlreadyInUse(Closure<?> closure, int numRetries) {
|
||||
try {
|
||||
closure.call()
|
||||
} catch (Throwable t) {
|
||||
// typically this is "java.net.BindException: Address already in use", but also can be
|
||||
// "io.netty.channel.unix.Errors$NativeIoException: bind() failed: Address already in use"
|
||||
if (numRetries == 0 || !t.getMessage().contains("Address already in use")) {
|
||||
throw t
|
||||
}
|
||||
log.debug("retrying due to bind exception: {}", t.getMessage(), t)
|
||||
withRetryOnAddressAlreadyInUse(closure, numRetries - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,74 +15,20 @@ import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEn
|
|||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
|
||||
import static org.junit.Assume.assumeTrue
|
||||
|
||||
import ch.qos.logback.classic.Level
|
||||
import io.opentelemetry.api.trace.Span
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||
import io.opentelemetry.instrumentation.test.AgentTestRunner
|
||||
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
||||
import io.opentelemetry.instrumentation.test.utils.OkHttpUtils
|
||||
import io.opentelemetry.instrumentation.test.utils.PortUtils
|
||||
import io.opentelemetry.sdk.trace.data.SpanData
|
||||
import java.util.concurrent.Callable
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.Response
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Unroll
|
||||
|
||||
@Unroll
|
||||
abstract class HttpServerTest<SERVER> extends AgentTestRunner {
|
||||
|
||||
public static final Logger SERVER_LOGGER = LoggerFactory.getLogger("http-server")
|
||||
static {
|
||||
((ch.qos.logback.classic.Logger) SERVER_LOGGER).setLevel(Level.DEBUG)
|
||||
}
|
||||
protected static final String TEST_CLIENT_IP = "1.1.1.1"
|
||||
protected static final String TEST_USER_AGENT = "test-user-agent"
|
||||
|
||||
@Shared
|
||||
SERVER server
|
||||
@Shared
|
||||
OkHttpClient client = OkHttpUtils.client()
|
||||
@Shared
|
||||
int port
|
||||
@Shared
|
||||
URI address
|
||||
|
||||
def setupSpec() {
|
||||
withRetryOnAddressAlreadyInUse({
|
||||
setupSpecUnderRetry()
|
||||
})
|
||||
}
|
||||
|
||||
def setupSpecUnderRetry() {
|
||||
port = PortUtils.randomOpenPort()
|
||||
address = buildAddress()
|
||||
server = startServer(port)
|
||||
println getClass().name + " http server started at: http://localhost:$port" + getContextPath()
|
||||
}
|
||||
|
||||
URI buildAddress() {
|
||||
return new URI("http://localhost:$port" + getContextPath() + "/")
|
||||
}
|
||||
|
||||
abstract SERVER startServer(int port)
|
||||
|
||||
def cleanupSpec() {
|
||||
if (server == null) {
|
||||
println getClass().name + " can't stop null server"
|
||||
return
|
||||
}
|
||||
stopServer(server)
|
||||
server = null
|
||||
println getClass().name + " http server stopped at: http://localhost:$port/"
|
||||
}
|
||||
|
||||
abstract void stopServer(SERVER server)
|
||||
abstract class HttpServerTest<SERVER> extends AgentTestRunner implements HttpServerTestTrait<SERVER> {
|
||||
|
||||
String expectedServerSpanName(ServerEndpoint endpoint) {
|
||||
return endpoint == PATH_PARAM ? getContextPath() + "/path/:id/param" : endpoint.resolvePath(address).path
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.test.base
|
||||
|
||||
import ch.qos.logback.classic.Level
|
||||
import io.opentelemetry.instrumentation.test.RetryOnAddressAlreadyInUseTrait
|
||||
import io.opentelemetry.instrumentation.test.utils.OkHttpUtils
|
||||
import io.opentelemetry.instrumentation.test.utils.PortUtils
|
||||
import okhttp3.OkHttpClient
|
||||
import org.junit.AfterClass
|
||||
import org.junit.BeforeClass
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
/**
|
||||
* A trait for testing requests against http server.
|
||||
*/
|
||||
trait HttpServerTestTrait<SERVER> implements RetryOnAddressAlreadyInUseTrait {
|
||||
static final Logger SERVER_LOGGER = LoggerFactory.getLogger("http-server")
|
||||
static {
|
||||
((ch.qos.logback.classic.Logger) SERVER_LOGGER).setLevel(Level.DEBUG)
|
||||
}
|
||||
static final String TEST_CLIENT_IP = "1.1.1.1"
|
||||
static final String TEST_USER_AGENT = "test-user-agent"
|
||||
|
||||
// not using SERVER as type because it triggers a bug in groovy and java joint compilation
|
||||
static Object server
|
||||
static OkHttpClient client = OkHttpUtils.client()
|
||||
static int port
|
||||
static URI address
|
||||
|
||||
@BeforeClass
|
||||
def setupServer() {
|
||||
withRetryOnAddressAlreadyInUse({
|
||||
setupSpecUnderRetry()
|
||||
})
|
||||
}
|
||||
|
||||
def setupSpecUnderRetry() {
|
||||
port = PortUtils.randomOpenPort()
|
||||
address = buildAddress()
|
||||
server = startServer(port)
|
||||
println getClass().name + " http server started at: http://localhost:$port" + getContextPath()
|
||||
}
|
||||
|
||||
URI buildAddress() {
|
||||
return new URI("http://localhost:$port" + getContextPath() + "/")
|
||||
}
|
||||
|
||||
abstract SERVER startServer(int port)
|
||||
|
||||
@AfterClass
|
||||
def cleanupServer() {
|
||||
if (server == null) {
|
||||
println getClass().name + " can't stop null server"
|
||||
return
|
||||
}
|
||||
stopServer(server)
|
||||
server = null
|
||||
println getClass().name + " http server stopped at: http://localhost:$port/"
|
||||
}
|
||||
|
||||
abstract void stopServer(SERVER server)
|
||||
|
||||
String getContextPath() {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue