From e7a6398c713fbb5bc0f7be07469630953dc841e8 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Wed, 31 Jul 2024 06:17:57 -0400 Subject: [PATCH] Convert jsp 2.3 tests from groovy to java (#11827) --- .../JspInstrumentationBasicTests.groovy | 539 ------------------ .../JspInstrumentationForwardTests.groovy | 490 ---------------- .../jsp/JspInstrumentationBasicTests.java | 488 ++++++++++++++++ .../jsp/JspInstrumentationForwardTests.java | 454 +++++++++++++++ .../instrumentation/jsp/JspSpan.java | 92 +++ .../jsp/JspSpanAssertionBuilder.java | 79 +++ .../jsp/JspSpanAssertions.java | 155 +++++ 7 files changed, 1268 insertions(+), 1029 deletions(-) delete mode 100644 instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTests.groovy delete mode 100644 instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationForwardTests.groovy create mode 100644 instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspInstrumentationBasicTests.java create mode 100644 instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspInstrumentationForwardTests.java create mode 100644 instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpan.java create mode 100644 instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpanAssertionBuilder.java create mode 100644 instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpanAssertions.java diff --git a/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTests.groovy b/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTests.groovy deleted file mode 100644 index 8990653a46d8..000000000000 --- a/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTests.groovy +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.instrumentation.test.utils.PortUtils -import io.opentelemetry.semconv.ExceptionAttributes -import io.opentelemetry.semconv.ServerAttributes -import io.opentelemetry.semconv.ClientAttributes -import io.opentelemetry.semconv.UserAgentAttributes -import io.opentelemetry.semconv.ErrorAttributes -import io.opentelemetry.semconv.HttpAttributes -import io.opentelemetry.semconv.NetworkAttributes -import io.opentelemetry.semconv.UrlAttributes -import io.opentelemetry.testing.internal.armeria.client.WebClient -import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse -import io.opentelemetry.testing.internal.armeria.common.HttpMethod -import io.opentelemetry.testing.internal.armeria.common.MediaType -import io.opentelemetry.testing.internal.armeria.common.RequestHeaders -import org.apache.catalina.Context -import org.apache.catalina.startup.Tomcat -import org.apache.jasper.JasperException -import spock.lang.Shared -import spock.lang.Unroll - -import java.nio.file.Files - -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -//TODO should this be HttpServerTest? -class JspInstrumentationBasicTests extends AgentInstrumentationSpecification { - - @Shared - int port - @Shared - Tomcat tomcatServer - @Shared - Context appContext - @Shared - String jspWebappContext = "jsptest-context" - - @Shared - File baseDir - @Shared - String baseUrl - - @Shared - WebClient client - - def setupSpec() { - baseDir = Files.createTempDirectory("jsp").toFile() - baseDir.deleteOnExit() - - port = PortUtils.findOpenPort() - - tomcatServer = new Tomcat() - tomcatServer.setBaseDir(baseDir.getAbsolutePath()) - tomcatServer.setPort(port) - tomcatServer.getConnector() - // comment to debug - tomcatServer.setSilent(true) - // this is needed in tomcat 9, this triggers the creation of a connector, will not - // affect tomcat 7 and 8 - // https://stackoverflow.com/questions/48998387/code-works-with-embedded-apache-tomcat-8-but-not-with-9-whats-changed - tomcatServer.getConnector() - baseUrl = "http://localhost:$port/$jspWebappContext" - client = WebClient.of(baseUrl) - - appContext = tomcatServer.addWebapp("/$jspWebappContext", - JspInstrumentationBasicTests.getResource("/webapps/jsptest").getPath()) - - tomcatServer.start() - System.out.println( - "Tomcat server: http://" + tomcatServer.getHost().getName() + ":" + port + "/") - } - - def cleanupSpec() { - tomcatServer.stop() - tomcatServer.destroy() - } - - @Unroll - def "non-erroneous GET #test test"() { - when: - AggregatedHttpResponse res = client.get("/${jspFileName}").aggregate().join() - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - def route = "/$jspWebappContext/$jspFileName" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /$jspFileName" - attributes { - "jsp.classFQCN" "org.apache.jsp.$jspClassNamePrefix$jspClassName" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /$jspFileName" - attributes { - "jsp.requestURL" "${baseUrl}/${jspFileName}" - } - } - } - } - res.status().code() == 200 - - where: - test | jspFileName | jspClassName | jspClassNamePrefix - "no java jsp" | "nojava.jsp" | "nojava_jsp" | "" - "basic loop jsp" | "common/loop.jsp" | "loop_jsp" | "common." - "invalid HTML markup" | "invalidMarkup.jsp" | "invalidMarkup_jsp" | "" - } - - def "non-erroneous GET with query string"() { - setup: - String queryString = "HELLO" - - when: - AggregatedHttpResponse res = client.get("/getQuery.jsp?${queryString}").aggregate().join() - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - def route = "/$jspWebappContext/getQuery.jsp" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$UrlAttributes.URL_QUERY" queryString - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /getQuery.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.getQuery_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /getQuery.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/getQuery.jsp" - } - } - } - } - res.status().code() == 200 - } - - def "non-erroneous POST"() { - setup: - RequestHeaders headers = RequestHeaders.builder(HttpMethod.POST, "/post.jsp") - .contentType(MediaType.FORM_DATA) - .build() - - when: - AggregatedHttpResponse res = client.execute(headers, "name=world").aggregate().join() - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - def route = "/$jspWebappContext/post.jsp" - - hasNoParent() - name "POST $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "POST" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /post.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.post_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /post.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/post.jsp" - } - } - } - } - res.status().code() == 200 - } - - @Unroll - def "erroneous runtime errors GET jsp with #test test"() { - when: - AggregatedHttpResponse res = client.get("/${jspFileName}").aggregate().join() - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - def route = "/$jspWebappContext/$jspFileName" - - hasNoParent() - name "GET $route" - kind SERVER - status ERROR - event(0) { - eventName("exception") - attributes { - "$ExceptionAttributes.EXCEPTION_TYPE" { String tagExceptionType -> - return tagExceptionType == exceptionClass.getName() || tagExceptionType.contains(exceptionClass.getSimpleName()) - } - "$ExceptionAttributes.EXCEPTION_MESSAGE" { String tagErrorMsg -> - return errorMessageOptional || tagErrorMsg instanceof String - } - "$ExceptionAttributes.EXCEPTION_STACKTRACE" String - } - } - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 500 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$ErrorAttributes.ERROR_TYPE" "500" - } - } - span(1) { - childOf span(0) - name "Compile /$jspFileName" - attributes { - "jsp.classFQCN" "org.apache.jsp.$jspClassName" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /$jspFileName" - status ERROR - event(0) { - eventName("exception") - attributes { - "$ExceptionAttributes.EXCEPTION_TYPE" { String tagExceptionType -> - return tagExceptionType == exceptionClass.getName() || tagExceptionType.contains(exceptionClass.getSimpleName()) - } - "$ExceptionAttributes.EXCEPTION_MESSAGE" { String tagErrorMsg -> - return errorMessageOptional || tagErrorMsg instanceof String - } - "$ExceptionAttributes.EXCEPTION_STACKTRACE" String - } - } - attributes { - "jsp.requestURL" "${baseUrl}/${jspFileName}" - } - } - } - } - res.status().code() == 500 - - where: - test | jspFileName | jspClassName | exceptionClass | errorMessageOptional - "java runtime error" | "runtimeError.jsp" | "runtimeError_jsp" | ArithmeticException | false - "invalid write" | "invalidWrite.jsp" | "invalidWrite_jsp" | IndexOutOfBoundsException | true - "missing query gives null" | "getQuery.jsp" | "getQuery_jsp" | NullPointerException | true - } - - def "non-erroneous include plain HTML GET"() { - when: - AggregatedHttpResponse res = client.get("/includes/includeHtml.jsp").aggregate().join() - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - def route = "/$jspWebappContext/includes/includeHtml.jsp" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /includes/includeHtml.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.includes.includeHtml_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /includes/includeHtml.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/includes/includeHtml.jsp" - } - } - } - } - res.status().code() == 200 - } - - def "non-erroneous multi GET"() { - when: - AggregatedHttpResponse res = client.get("/includes/includeMulti.jsp").aggregate().join() - - then: - assertTraces(1) { - trace(0, 7) { - span(0) { - def route = "/$jspWebappContext/includes/includeMulti.jsp" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /includes/includeMulti.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.includes.includeMulti_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /includes/includeMulti.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp" - } - } - span(3) { - childOf span(2) - name "Compile /common/javaLoopH2.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(4) { - childOf span(2) - name "Render /common/javaLoopH2.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp" - } - } - span(5) { - childOf span(2) - name "Compile /common/javaLoopH2.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(6) { - childOf span(2) - name "Render /common/javaLoopH2.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp" - } - } - } - } - res.status().code() == 200 - } - - def "#test compile error should not produce render traces and spans"() { - when: - AggregatedHttpResponse res = client.get("/${jspFileName}").aggregate().join() - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - def route = "/$jspWebappContext/$jspFileName" - - hasNoParent() - name "GET $route" - kind SERVER - status ERROR - errorEvent(JasperException, String) - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 500 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$ErrorAttributes.ERROR_TYPE" "500" - } - } - span(1) { - childOf span(0) - name "Compile /$jspFileName" - status ERROR - errorEvent(JasperException, String) - attributes { - "jsp.classFQCN" "org.apache.jsp.$jspClassNamePrefix$jspClassName" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - } - } - res.status().code() == 500 - - where: - test | jspFileName | jspClassName | jspClassNamePrefix - "normal" | "compileError.jsp" | "compileError_jsp" | "" - "forward" | "forwards/forwardWithCompileError.jsp" | "forwardWithCompileError_jsp" | "forwards." - } - - def "direct static file reference"() { - when: - AggregatedHttpResponse res = client.get("/${staticFile}").aggregate().join() - - then: - res.status().code() == 200 - assertTraces(1) { - trace(0, 1) { - span(0) { - def route = "/$jspWebappContext/*" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" "/$jspWebappContext/$staticFile" - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - } - } - - where: - staticFile = "common/hello.html" - } -} diff --git a/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationForwardTests.groovy b/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationForwardTests.groovy deleted file mode 100644 index baaa5bc4f8f9..000000000000 --- a/instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationForwardTests.groovy +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.instrumentation.test.utils.PortUtils -import io.opentelemetry.semconv.ServerAttributes -import io.opentelemetry.semconv.ClientAttributes -import io.opentelemetry.semconv.UserAgentAttributes -import io.opentelemetry.semconv.ErrorAttributes -import io.opentelemetry.semconv.HttpAttributes -import io.opentelemetry.semconv.NetworkAttributes -import io.opentelemetry.semconv.UrlAttributes -import io.opentelemetry.testing.internal.armeria.client.WebClient -import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse -import org.apache.catalina.Context -import org.apache.catalina.startup.Tomcat -import org.apache.jasper.JasperException -import spock.lang.Shared -import spock.lang.Unroll - -import java.nio.file.Files - -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR -import static io.opentelemetry.api.trace.StatusCode.UNSET - -class JspInstrumentationForwardTests extends AgentInstrumentationSpecification { - - @Shared - int port - @Shared - Tomcat tomcatServer - @Shared - Context appContext - @Shared - String jspWebappContext = "jsptest-context" - - @Shared - File baseDir - @Shared - String baseUrl - - @Shared - WebClient client - - def setupSpec() { - baseDir = Files.createTempDirectory("jsp").toFile() - baseDir.deleteOnExit() - - port = PortUtils.findOpenPort() - - tomcatServer = new Tomcat() - tomcatServer.setBaseDir(baseDir.getAbsolutePath()) - tomcatServer.setPort(port) - tomcatServer.getConnector() - // comment to debug - tomcatServer.setSilent(true) - // this is needed in tomcat 9, this triggers the creation of a connector, will not - // affect tomcat 7 and 8 - // https://stackoverflow.com/questions/48998387/code-works-with-embedded-apache-tomcat-8-but-not-with-9-whats-changed - tomcatServer.getConnector() - - baseUrl = "http://localhost:$port/$jspWebappContext" - client = WebClient.of(baseUrl) - - appContext = tomcatServer.addWebapp("/$jspWebappContext", - JspInstrumentationForwardTests.getResource("/webapps/jsptest").getPath()) - - tomcatServer.start() - System.out.println( - "Tomcat server: http://" + tomcatServer.getHost().getName() + ":" + port + "/") - } - - def cleanupSpec() { - tomcatServer.stop() - tomcatServer.destroy() - } - - @Unroll - def "non-erroneous GET forward to #forwardTo"() { - when: - AggregatedHttpResponse res = client.get("/$forwardFromFileName").aggregate().join() - - then: - assertTraces(1) { - trace(0, 5) { - span(0) { - def route = "/$jspWebappContext/$forwardFromFileName" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /$forwardFromFileName" - attributes { - "jsp.classFQCN" "org.apache.jsp.$jspForwardFromClassPrefix$jspForwardFromClassName" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /$forwardFromFileName" - attributes { - "jsp.requestURL" "${baseUrl}/$forwardFromFileName" - } - } - span(3) { - childOf span(2) - name "Compile /$forwardDestFileName" - attributes { - "jsp.classFQCN" "org.apache.jsp.$jspForwardDestClassPrefix$jspForwardDestClassName" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(4) { - childOf span(2) - name "Render /$forwardDestFileName" - attributes { - "jsp.forwardOrigin" "/$forwardFromFileName" - "jsp.requestURL" "${baseUrl}/$forwardDestFileName" - } - } - } - } - res.status().code() == 200 - - where: - forwardTo | forwardFromFileName | forwardDestFileName | jspForwardFromClassName | jspForwardFromClassPrefix | jspForwardDestClassName | jspForwardDestClassPrefix - "no java jsp" | "forwards/forwardToNoJavaJsp.jsp" | "nojava.jsp" | "forwardToNoJavaJsp_jsp" | "forwards." | "nojava_jsp" | "" - "normal java jsp" | "forwards/forwardToSimpleJava.jsp" | "common/loop.jsp" | "forwardToSimpleJava_jsp" | "forwards." | "loop_jsp" | "common." - } - - def "non-erroneous GET forward to plain HTML"() { - when: - AggregatedHttpResponse res = client.get("/forwards/forwardToHtml.jsp").aggregate().join() - - then: - assertTraces(1) { - trace(0, 3) { - span(0) { - def route = "/$jspWebappContext/forwards/forwardToHtml.jsp" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /forwards/forwardToHtml.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.forwards.forwardToHtml_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /forwards/forwardToHtml.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/forwards/forwardToHtml.jsp" - } - } - } - } - res.status().code() == 200 - } - - def "non-erroneous GET forwarded to jsp with multiple includes"() { - when: - AggregatedHttpResponse res = client.get("/forwards/forwardToIncludeMulti.jsp").aggregate().join() - - then: - assertTraces(1) { - trace(0, 9) { - span(0) { - def route = "/$jspWebappContext/forwards/forwardToIncludeMulti.jsp" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /forwards/forwardToIncludeMulti.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.forwards.forwardToIncludeMulti_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /forwards/forwardToIncludeMulti.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/forwards/forwardToIncludeMulti.jsp" - } - } - span(3) { - childOf span(2) - name "Compile /includes/includeMulti.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.includes.includeMulti_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(4) { - childOf span(2) - name "Render /includes/includeMulti.jsp" - attributes { - "jsp.forwardOrigin" "/forwards/forwardToIncludeMulti.jsp" - "jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp" - } - } - span(5) { - childOf span(4) - name "Compile /common/javaLoopH2.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(6) { - childOf span(4) - name "Render /common/javaLoopH2.jsp" - attributes { - "jsp.forwardOrigin" "/forwards/forwardToIncludeMulti.jsp" - "jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp" - } - } - span(7) { - childOf span(4) - name "Compile /common/javaLoopH2.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(8) { - childOf span(4) - name "Render /common/javaLoopH2.jsp" - attributes { - "jsp.forwardOrigin" "/forwards/forwardToIncludeMulti.jsp" - "jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp" - } - } - } - } - res.status().code() == 200 - } - - def "non-erroneous GET forward to another forward (2 forwards)"() { - when: - AggregatedHttpResponse res = client.get("/forwards/forwardToJspForward.jsp").aggregate().join() - - then: - assertTraces(1) { - trace(0, 7) { - span(0) { - def route = "/$jspWebappContext/forwards/forwardToJspForward.jsp" - - hasNoParent() - name "GET $route" - kind SERVER - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /forwards/forwardToJspForward.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.forwards.forwardToJspForward_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /forwards/forwardToJspForward.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/forwards/forwardToJspForward.jsp" - } - } - span(3) { - childOf span(2) - name "Compile /forwards/forwardToSimpleJava.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.forwards.forwardToSimpleJava_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(4) { - childOf span(2) - name "Render /forwards/forwardToSimpleJava.jsp" - attributes { - "jsp.forwardOrigin" "/forwards/forwardToJspForward.jsp" - "jsp.requestURL" "${baseUrl}/forwards/forwardToSimpleJava.jsp" - } - } - span(5) { - childOf span(4) - name "Compile /common/loop.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.common.loop_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(6) { - childOf span(4) - name "Render /common/loop.jsp" - attributes { - "jsp.forwardOrigin" "/forwards/forwardToJspForward.jsp" - "jsp.requestURL" "${baseUrl}/common/loop.jsp" - } - } - } - } - res.status().code() == 200 - } - - def "forward to jsp with compile error should not produce a 2nd render span"() { - when: - AggregatedHttpResponse res = client.get("/forwards/forwardToCompileError.jsp").aggregate().join() - - then: - assertTraces(1) { - trace(0, 4) { - span(0) { - def route = "/$jspWebappContext/forwards/forwardToCompileError.jsp" - - hasNoParent() - name "GET $route" - kind SERVER - status ERROR - errorEvent(JasperException, String) - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 500 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - "$ErrorAttributes.ERROR_TYPE" "500" - } - } - span(1) { - childOf span(0) - name "Compile /forwards/forwardToCompileError.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.forwards.forwardToCompileError_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /forwards/forwardToCompileError.jsp" - status ERROR - errorEvent(JasperException, String) - attributes { - "jsp.requestURL" "${baseUrl}/forwards/forwardToCompileError.jsp" - } - } - span(3) { - childOf span(2) - name "Compile /compileError.jsp" - status ERROR - errorEvent(JasperException, String) - attributes { - "jsp.classFQCN" "org.apache.jsp.compileError_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - } - } - res.status().code() == 500 - } - - def "forward to non existent jsp should be 404"() { - when: - AggregatedHttpResponse res = client.get("/forwards/forwardToNonExistent.jsp").aggregate().join() - - then: - assertTraces(1) { - trace(0, 4) { - span(0) { - def route = "/$jspWebappContext/forwards/forwardToNonExistent.jsp" - - hasNoParent() - name "GET $route" - kind SERVER - status UNSET - attributes { - "$UrlAttributes.URL_SCHEME" "http" - "$UrlAttributes.URL_PATH" route - "$HttpAttributes.HTTP_REQUEST_METHOD" "GET" - "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 404 - "$UserAgentAttributes.USER_AGENT_ORIGINAL" String - "$HttpAttributes.HTTP_ROUTE" route - "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1" - "$ServerAttributes.SERVER_ADDRESS" "localhost" - "$ServerAttributes.SERVER_PORT" port - "$ClientAttributes.CLIENT_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1" - "$NetworkAttributes.NETWORK_PEER_PORT" Long - } - } - span(1) { - childOf span(0) - name "Compile /forwards/forwardToNonExistent.jsp" - attributes { - "jsp.classFQCN" "org.apache.jsp.forwards.forwardToNonExistent_jsp" - "jsp.compiler" "org.apache.jasper.compiler.JDTCompiler" - } - } - span(2) { - childOf span(0) - name "Render /forwards/forwardToNonExistent.jsp" - attributes { - "jsp.requestURL" "${baseUrl}/forwards/forwardToNonExistent.jsp" - } - } - span(3) { - childOf span(2) - name "ResponseFacade.sendError" - } - } - } - res.status().code() == 404 - } -} diff --git a/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspInstrumentationBasicTests.java b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspInstrumentationBasicTests.java new file mode 100644 index 000000000000..2114818ce287 --- /dev/null +++ b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspInstrumentationBasicTests.java @@ -0,0 +1,488 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jsp; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerUsingTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.ClientAttributes; +import io.opentelemetry.semconv.ExceptionAttributes; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.NetworkAttributes; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.UrlAttributes; +import io.opentelemetry.semconv.UserAgentAttributes; +import io.opentelemetry.testing.internal.armeria.client.WebClient; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import io.opentelemetry.testing.internal.armeria.common.HttpMethod; +import io.opentelemetry.testing.internal.armeria.common.MediaType; +import io.opentelemetry.testing.internal.armeria.common.RequestHeaders; +import java.io.File; +import java.nio.file.Files; +import java.util.stream.Stream; +import org.apache.catalina.startup.Tomcat; +import org.apache.jasper.JasperException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.ValueSource; + +class JspInstrumentationBasicTests extends AbstractHttpServerUsingTest { + + @RegisterExtension + public static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forAgent(); + + private static JspSpanAssertions spanAsserts; + + @Override + protected Tomcat setupServer() throws Exception { + File baseDir = Files.createTempDirectory("jsp").toFile(); + baseDir.deleteOnExit(); + + Tomcat tomcatServer = new Tomcat(); + tomcatServer.setBaseDir(baseDir.getAbsolutePath()); + tomcatServer.setPort(port); + tomcatServer.getConnector(); + + // comment to debug + tomcatServer.setSilent(true); + + // this is needed in tomcat 9, this triggers the creation of a connector, will not + // affect tomcat 7 and 8 + // https://stackoverflow.com/questions/48998387/code-works-with-embedded-apache-tomcat-8-but-not-with-9-whats-changed + tomcatServer.getConnector(); + + String baseUrl = "http://localhost:" + port + "/" + getContextPath(); + spanAsserts = new JspSpanAssertions(baseUrl, port); + client = WebClient.of(baseUrl); + + tomcatServer.addWebapp( + "/" + getContextPath(), + JspInstrumentationBasicTests.class.getResource("/webapps/jsptest").getPath()); + + tomcatServer.start(); + return tomcatServer; + } + + @Override + protected void stopServer(Tomcat tomcat) throws Exception { + tomcat.stop(); + tomcat.destroy(); + } + + @Override + protected String getContextPath() { + return "jsptest-context"; + } + + @BeforeAll + protected void setUp() { + startServer(); + } + + @AfterAll + protected void cleanUp() { + cleanupServer(); + } + + @ParameterizedTest(name = "GET {0}") + @ArgumentsSource(NonErroneousArgs.class) + void testNonErroneousGet( + String testName, String jspFileName, String jspClassName, String jspClassNamePrefix) { + AggregatedHttpResponse res = client.get(jspFileName).aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute("/" + getContextPath() + jspFileName) + .withResponseStatus(200) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute(jspFileName) + .withClassName(jspClassNamePrefix + jspClassName) + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute(jspFileName) + .build()))); + } + + static class NonErroneousArgs implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of("no java jsp", "/nojava.jsp", "nojava_jsp", ""), + Arguments.of("basic loop jsp", "/common/loop.jsp", "loop_jsp", "common."), + Arguments.of("invalid HTML markup", "/invalidMarkup.jsp", "invalidMarkup_jsp", "")); + } + } + + @Test + void testNonErroneousGetWithQueryString() { + String queryString = "HELLO"; + String route = "/" + getContextPath() + "/getQuery.jsp"; + + AggregatedHttpResponse res = client.get("/getQuery.jsp?" + queryString).aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET " + route) + .hasNoParent() + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo(UrlAttributes.URL_SCHEME, "http"), + equalTo(UrlAttributes.URL_PATH, route), + equalTo(UrlAttributes.URL_QUERY, queryString), + equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"), + equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200), + satisfies( + UserAgentAttributes.USER_AGENT_ORIGINAL, + val -> val.isInstanceOf(String.class)), + equalTo(HttpAttributes.HTTP_ROUTE, route), + equalTo(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + equalTo(ServerAttributes.SERVER_PORT, port), + equalTo(ClientAttributes.CLIENT_ADDRESS, "127.0.0.1"), + equalTo(NetworkAttributes.NETWORK_PEER_ADDRESS, "127.0.0.1"), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + val -> val.isInstanceOf(Long.class))), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/getQuery.jsp") + .withClassName("getQuery_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/getQuery.jsp") + .build()))); + } + + @Test + void testNonErroneousPost() { + RequestHeaders headers = + RequestHeaders.builder(HttpMethod.POST, "/post.jsp") + .contentType(MediaType.FORM_DATA) + .build(); + + AggregatedHttpResponse res = client.execute(headers, "name=world").aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("POST") + .withRoute("/" + getContextPath() + "/post.jsp") + .withResponseStatus(200) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/post.jsp") + .withClassName("post_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/post.jsp") + .build()))); + } + + @ParameterizedTest(name = "GET jsp with {0}") + @ArgumentsSource(ErroneousRuntimeErrorsArgs.class) + void testErroneousRuntimeErrorsGet( + String testName, + String jspFileName, + String jspClassName, + Class exceptionClass, + boolean errorMessageOptional) { + AggregatedHttpResponse res = client.get(jspFileName).aggregate().join(); + assertThat(res.status().code()).isEqualTo(500); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute("/" + getContextPath() + jspFileName) + .withResponseStatus(500) + .withExceptionClass(exceptionClass) + .withErrorMessageOptional(errorMessageOptional) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute(jspFileName) + .withClassName(jspClassName) + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute(jspFileName) + .withErrorMessageOptional(errorMessageOptional) + .build()))); + } + + static class ErroneousRuntimeErrorsArgs implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of( + "java runtime error", + "/runtimeError.jsp", + "runtimeError_jsp", + ArithmeticException.class, + false), + Arguments.of( + "invalid write", + "/invalidWrite.jsp", + "invalidWrite_jsp", + IndexOutOfBoundsException.class, + true), + Arguments.of( + "invalid write", "/getQuery.jsp", "getQuery_jsp", NullPointerException.class, true)); + } + } + + @Test + void testNonErroneousIncludePlainHtmlGet() { + AggregatedHttpResponse res = client.get("/includes/includeHtml.jsp").aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute("/" + getContextPath() + "/includes/includeHtml.jsp") + .withResponseStatus(200) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/includes/includeHtml.jsp") + .withClassName("includes.includeHtml_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/includes/includeHtml.jsp") + .build()))); + } + + @Test + void testNonErroneousMultiGet() { + AggregatedHttpResponse res = client.get("/includes/includeMulti.jsp").aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute("/" + getContextPath() + "/includes/includeMulti.jsp") + .withResponseStatus(200) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/includes/includeMulti.jsp") + .withClassName("includes.includeMulti_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/includes/includeMulti.jsp") + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/common/javaLoopH2.jsp") + .withClassName("common.javaLoopH2_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/common/javaLoopH2.jsp") + .withRequestUrlOverride("/includes/includeMulti.jsp") + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/common/javaLoopH2.jsp") + .withClassName("common.javaLoopH2_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/common/javaLoopH2.jsp") + .withRequestUrlOverride("/includes/includeMulti.jsp") + .build()))); + } + + @ParameterizedTest + @ArgumentsSource(CompileErrorsArgs.class) + void testCompileErrorShouldNotProduceRenderTracesAndSpans( + String jspFileName, String jspClassName, String jspClassNamePrefix) { + AggregatedHttpResponse res = client.get(jspFileName).aggregate().join(); + assertThat(res.status().code()).isEqualTo(500); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute("/" + getContextPath() + jspFileName) + .withResponseStatus(500) + .withExceptionClass(JasperException.class) + .build()), + span -> + span.hasName("Compile " + jspFileName) + .hasParent(trace.getSpan(0)) + .hasStatus(StatusData.error()) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfyingExactly( + equalTo( + ExceptionAttributes.EXCEPTION_TYPE, + JasperException.class.getCanonicalName()), + satisfies( + ExceptionAttributes.EXCEPTION_STACKTRACE, + val -> val.isInstanceOf(String.class)), + satisfies( + ExceptionAttributes.EXCEPTION_MESSAGE, + val -> val.isInstanceOf(String.class)))) + .hasAttributesSatisfyingExactly( + equalTo( + stringKey("jsp.classFQCN"), + "org.apache.jsp." + jspClassNamePrefix + jspClassName), + equalTo( + stringKey("jsp.compiler"), + "org.apache.jasper.compiler.JDTCompiler")))); + } + + static class CompileErrorsArgs implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of("/compileError.jsp", "compileError_jsp", ""), + Arguments.of( + "/forwards/forwardWithCompileError.jsp", "forwardWithCompileError_jsp", "forwards.")); + } + } + + @ParameterizedTest + @ValueSource(strings = {"/common/hello.html"}) + void testDirectStaticFileReference(String staticFile) { + String route = "/" + getContextPath() + "/*"; + + AggregatedHttpResponse res = client.get(staticFile).aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET " + route) + .hasNoParent() + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo(UrlAttributes.URL_SCHEME, "http"), + equalTo(UrlAttributes.URL_PATH, "/" + getContextPath() + staticFile), + equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"), + equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200), + satisfies( + UserAgentAttributes.USER_AGENT_ORIGINAL, + val -> val.isInstanceOf(String.class)), + equalTo(HttpAttributes.HTTP_ROUTE, route), + equalTo(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + equalTo(ServerAttributes.SERVER_PORT, port), + equalTo(ClientAttributes.CLIENT_ADDRESS, "127.0.0.1"), + equalTo(NetworkAttributes.NETWORK_PEER_ADDRESS, "127.0.0.1"), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + val -> val.isInstanceOf(Long.class))))); + } +} diff --git a/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspInstrumentationForwardTests.java b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspInstrumentationForwardTests.java new file mode 100644 index 000000000000..5ba8d5df7c49 --- /dev/null +++ b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspInstrumentationForwardTests.java @@ -0,0 +1,454 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jsp; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerUsingTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.ClientAttributes; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.NetworkAttributes; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.UrlAttributes; +import io.opentelemetry.semconv.UserAgentAttributes; +import io.opentelemetry.testing.internal.armeria.client.WebClient; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import java.io.File; +import java.nio.file.Files; +import java.util.stream.Stream; +import org.apache.catalina.startup.Tomcat; +import org.apache.jasper.JasperException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +class JspInstrumentationForwardTests extends AbstractHttpServerUsingTest { + + @RegisterExtension + public static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forAgent(); + + private static JspSpanAssertions spanAsserts; + + @Override + protected Tomcat setupServer() throws Exception { + File baseDir = Files.createTempDirectory("jsp").toFile(); + baseDir.deleteOnExit(); + + Tomcat tomcatServer = new Tomcat(); + tomcatServer.setBaseDir(baseDir.getAbsolutePath()); + tomcatServer.setPort(port); + tomcatServer.getConnector(); + + // comment to debug + tomcatServer.setSilent(true); + + // this is needed in tomcat 9, this triggers the creation of a connector, will not + // affect tomcat 7 and 8 + // https://stackoverflow.com/questions/48998387/code-works-with-embedded-apache-tomcat-8-but-not-with-9-whats-changed + tomcatServer.getConnector(); + + String baseUrl = "http://localhost:" + port + "/" + getContextPath(); + spanAsserts = new JspSpanAssertions(baseUrl, port); + client = WebClient.of(baseUrl); + + tomcatServer.addWebapp( + "/" + getContextPath(), + JspInstrumentationForwardTests.class.getResource("/webapps/jsptest").getPath()); + + tomcatServer.start(); + return tomcatServer; + } + + @Override + protected void stopServer(Tomcat tomcat) throws Exception { + tomcat.stop(); + tomcat.destroy(); + } + + @Override + protected String getContextPath() { + return "jsptest-context"; + } + + @BeforeAll + protected void setUp() { + startServer(); + } + + @AfterAll + protected void cleanUp() { + cleanupServer(); + } + + @ParameterizedTest(name = "Forward to {0}") + @ArgumentsSource(NonErroneousGetForwardArgs.class) + void testNonErroneousGetForwardTo( + String name, + String forwardFromFileName, + String forwardDestFileName, + String jspForwardFromClassName, + String jspForwardFromClassPrefix, + String jspForwardDestClassName, + String jspForwardDestClassPrefix) { + AggregatedHttpResponse res = client.get(forwardFromFileName).aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute("/" + getContextPath() + forwardFromFileName) + .withResponseStatus(200) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute(forwardFromFileName) + .withClassName(jspForwardFromClassPrefix + jspForwardFromClassName) + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute(forwardFromFileName) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute(forwardDestFileName) + .withClassName(jspForwardDestClassPrefix + jspForwardDestClassName) + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute(forwardDestFileName) + .build()))); + } + + static class NonErroneousGetForwardArgs implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of( + "no java jsp", + "/forwards/forwardToNoJavaJsp.jsp", + "/nojava.jsp", + "forwardToNoJavaJsp_jsp", + "forwards.", + "nojava_jsp", + ""), + Arguments.of( + "normal java jsp", + "/forwards/forwardToSimpleJava.jsp", + "/common/loop.jsp", + "forwardToSimpleJava_jsp", + "forwards.", + "loop_jsp", + "common.")); + } + } + + @Test + void testNonErroneousGetForwardToPlainHtml() { + AggregatedHttpResponse res = client.get("/forwards/forwardToHtml.jsp").aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute("/" + getContextPath() + "/forwards/forwardToHtml.jsp") + .withResponseStatus(200) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToHtml.jsp") + .withClassName("forwards.forwardToHtml_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToHtml.jsp") + .build()))); + } + + @Test + void testNonErroneousGetForwardedToJspWithMultipleIncludes() { + AggregatedHttpResponse res = + client.get("/forwards/forwardToIncludeMulti.jsp").aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute( + "/" + getContextPath() + "/forwards/forwardToIncludeMulti.jsp") + .withResponseStatus(200) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToIncludeMulti.jsp") + .withClassName("forwards.forwardToIncludeMulti_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToIncludeMulti.jsp") + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/includes/includeMulti.jsp") + .withClassName("includes.includeMulti_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/includes/includeMulti.jsp") + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(4)) + .withRoute("/common/javaLoopH2.jsp") + .withClassName("common.javaLoopH2_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(4)) + .withRoute("/common/javaLoopH2.jsp") + .withRequestUrlOverride("/includes/includeMulti.jsp") + .withForwardOrigin("/forwards/forwardToIncludeMulti.jsp") + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(4)) + .withRoute("/common/javaLoopH2.jsp") + .withClassName("common.javaLoopH2_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(4)) + .withRoute("/common/javaLoopH2.jsp") + .withRequestUrlOverride("/includes/includeMulti.jsp") + .withForwardOrigin("/forwards/forwardToIncludeMulti.jsp") + .build()))); + } + + @Test + void testNonErroneousGetForwardToAnotherForward() { + AggregatedHttpResponse res = client.get("/forwards/forwardToJspForward.jsp").aggregate().join(); + assertThat(res.status().code()).isEqualTo(200); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute("/" + getContextPath() + "/forwards/forwardToJspForward.jsp") + .withResponseStatus(200) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToJspForward.jsp") + .withClassName("forwards.forwardToJspForward_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToJspForward.jsp") + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/forwards/forwardToSimpleJava.jsp") + .withClassName("forwards.forwardToSimpleJava_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/forwards/forwardToSimpleJava.jsp") + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(4)) + .withRoute("/common/loop.jsp") + .withClassName("common.loop_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(4)) + .withRoute("/common/loop.jsp") + .build()))); + } + + @Test + void testForwardToJspWithCompileErrorShouldNotProduceSecondRenderSpan() { + AggregatedHttpResponse res = + client.get("/forwards/forwardToCompileError.jsp").aggregate().join(); + assertThat(res.status().code()).isEqualTo(500); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + spanAsserts.assertServerSpan( + span, + new JspSpanAssertionBuilder() + .withMethod("GET") + .withRoute( + "/" + getContextPath() + "/forwards/forwardToCompileError.jsp") + .withResponseStatus(500) + .withExceptionClass(JasperException.class) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToCompileError.jsp") + .withClassName("forwards.forwardToCompileError_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToCompileError.jsp") + .withExceptionClass(JasperException.class) + .build()), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(2)) + .withRoute("/compileError.jsp") + .withClassName("compileError_jsp") + .withExceptionClass(JasperException.class) + .build()))); + } + + @Test + void testForwardToNonExistentJspShouldBe404() { + String route = "/" + getContextPath() + "/forwards/forwardToNonExistent.jsp"; + + AggregatedHttpResponse res = + client.get("/forwards/forwardToNonExistent.jsp").aggregate().join(); + assertThat(res.status().code()).isEqualTo(404); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET " + route) + .hasNoParent() + .hasKind(SpanKind.SERVER) + .hasStatus(StatusData.unset()) + .hasAttributesSatisfyingExactly( + equalTo(UrlAttributes.URL_SCHEME, "http"), + equalTo(UrlAttributes.URL_PATH, route), + equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"), + equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 404), + satisfies( + UserAgentAttributes.USER_AGENT_ORIGINAL, + val -> val.isInstanceOf(String.class)), + equalTo(HttpAttributes.HTTP_ROUTE, route), + equalTo(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + equalTo(ServerAttributes.SERVER_PORT, port), + equalTo(ClientAttributes.CLIENT_ADDRESS, "127.0.0.1"), + equalTo(NetworkAttributes.NETWORK_PEER_ADDRESS, "127.0.0.1"), + satisfies( + NetworkAttributes.NETWORK_PEER_PORT, + val -> val.isInstanceOf(Long.class))), + span -> + spanAsserts.assertCompileSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToNonExistent.jsp") + .withClassName("forwards.forwardToNonExistent_jsp") + .build()), + span -> + spanAsserts.assertRenderSpan( + span, + new JspSpanAssertionBuilder() + .withParent(trace.getSpan(0)) + .withRoute("/forwards/forwardToNonExistent.jsp") + .build()), + span -> span.hasName("ResponseFacade.sendError").hasParent(trace.getSpan(2)))); + } +} diff --git a/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpan.java b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpan.java new file mode 100644 index 000000000000..094b047087d0 --- /dev/null +++ b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpan.java @@ -0,0 +1,92 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jsp; + +import io.opentelemetry.sdk.trace.data.SpanData; + +class JspSpan { + private SpanData parent; + private String method; + private String className; + private String requestUrlOverride; + private String forwardOrigin; + private String route; + private int responseStatus; + private Class exceptionClass; + private boolean errorMessageOptional; + + public SpanData getParent() { + return parent; + } + + public void setParent(SpanData parent) { + this.parent = parent; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getRequestUrlOverride() { + return requestUrlOverride; + } + + public void setRequestUrlOverride(String requestUrlOverride) { + this.requestUrlOverride = requestUrlOverride; + } + + public String getForwardOrigin() { + return forwardOrigin; + } + + public void setForwardOrigin(String forwardOrigin) { + this.forwardOrigin = forwardOrigin; + } + + public String getRoute() { + return route; + } + + public void setRoute(String route) { + this.route = route; + } + + public int getResponseStatus() { + return responseStatus; + } + + public void setResponseStatus(int responseStatus) { + this.responseStatus = responseStatus; + } + + public Class getExceptionClass() { + return exceptionClass; + } + + public void setExceptionClass(Class exceptionClass) { + this.exceptionClass = exceptionClass; + } + + public boolean getErrorMessageOptional() { + return errorMessageOptional; + } + + public void setErrorMessageOptional(boolean errorMessageOptional) { + this.errorMessageOptional = errorMessageOptional; + } +} diff --git a/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpanAssertionBuilder.java b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpanAssertionBuilder.java new file mode 100644 index 000000000000..b24f592495c4 --- /dev/null +++ b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpanAssertionBuilder.java @@ -0,0 +1,79 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jsp; + +import io.opentelemetry.sdk.trace.data.SpanData; + +class JspSpanAssertionBuilder { + private SpanData parent; + private String method; + private String route; + private String className; + private String requestUrlOverride; + private String forwardOrigin; + private int responseStatus; + private Class exceptionClass; + private boolean errorMessageOptional; + + public JspSpanAssertionBuilder withParent(SpanData parent) { + this.parent = parent; + return this; + } + + public JspSpanAssertionBuilder withMethod(String method) { + this.method = method; + return this; + } + + public JspSpanAssertionBuilder withRoute(String route) { + this.route = route; + return this; + } + + public JspSpanAssertionBuilder withClassName(String className) { + this.className = className; + return this; + } + + public JspSpanAssertionBuilder withRequestUrlOverride(String requestUrlOverride) { + this.requestUrlOverride = requestUrlOverride; + return this; + } + + public JspSpanAssertionBuilder withForwardOrigin(String forwardOrigin) { + this.forwardOrigin = forwardOrigin; + return this; + } + + public JspSpanAssertionBuilder withResponseStatus(int responseStatus) { + this.responseStatus = responseStatus; + return this; + } + + public JspSpanAssertionBuilder withExceptionClass(Class exceptionClass) { + this.exceptionClass = exceptionClass; + return this; + } + + public JspSpanAssertionBuilder withErrorMessageOptional(boolean errorMessageOptional) { + this.errorMessageOptional = errorMessageOptional; + return this; + } + + public JspSpan build() { + JspSpan serverSpan = new JspSpan(); + serverSpan.setParent(this.parent); + serverSpan.setMethod(this.method); + serverSpan.setRoute(this.route); + serverSpan.setClassName(this.className); + serverSpan.setRequestUrlOverride(this.requestUrlOverride); + serverSpan.setForwardOrigin(this.forwardOrigin); + serverSpan.setResponseStatus(this.responseStatus); + serverSpan.setExceptionClass(this.exceptionClass); + serverSpan.setErrorMessageOptional(this.errorMessageOptional); + return serverSpan; + } +} diff --git a/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpanAssertions.java b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpanAssertions.java new file mode 100644 index 000000000000..7b9baa32c697 --- /dev/null +++ b/instrumentation/jsp-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jsp/JspSpanAssertions.java @@ -0,0 +1,155 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jsp; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.ClientAttributes; +import io.opentelemetry.semconv.ErrorAttributes; +import io.opentelemetry.semconv.ExceptionAttributes; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.NetworkAttributes; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.UrlAttributes; +import io.opentelemetry.semconv.UserAgentAttributes; + +class JspSpanAssertions { + private final String baseUrl; + private final int port; + + JspSpanAssertions(String baseUrl, int port) { + this.baseUrl = baseUrl; + this.port = port; + } + + SpanDataAssert assertServerSpan(SpanDataAssert span, JspSpan spanData) { + if (spanData.getExceptionClass() != null) { + span.hasStatus(StatusData.error()) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfyingExactly( + satisfies( + ExceptionAttributes.EXCEPTION_TYPE, + val -> + val.satisfiesAnyOf( + v -> val.isEqualTo(spanData.getExceptionClass().getName()), + v -> + val.contains( + spanData.getExceptionClass().getSimpleName()))), + satisfies( + ExceptionAttributes.EXCEPTION_MESSAGE, + val -> + val.satisfiesAnyOf( + v -> assertThat(spanData.getErrorMessageOptional()).isTrue(), + v -> val.isInstanceOf(String.class))), + satisfies( + ExceptionAttributes.EXCEPTION_STACKTRACE, + val -> val.isInstanceOf(String.class)))); + } + + return span.hasName(spanData.getMethod() + " " + spanData.getRoute()) + .hasNoParent() + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo(UrlAttributes.URL_SCHEME, "http"), + equalTo(UrlAttributes.URL_PATH, spanData.getRoute()), + equalTo(HttpAttributes.HTTP_REQUEST_METHOD, spanData.getMethod()), + equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, spanData.getResponseStatus()), + satisfies( + UserAgentAttributes.USER_AGENT_ORIGINAL, val -> val.isInstanceOf(String.class)), + equalTo(HttpAttributes.HTTP_ROUTE, spanData.getRoute()), + equalTo(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1"), + equalTo(ServerAttributes.SERVER_ADDRESS, "localhost"), + equalTo(ServerAttributes.SERVER_PORT, port), + equalTo(ClientAttributes.CLIENT_ADDRESS, "127.0.0.1"), + equalTo(NetworkAttributes.NETWORK_PEER_ADDRESS, "127.0.0.1"), + satisfies(NetworkAttributes.NETWORK_PEER_PORT, val -> val.isInstanceOf(Long.class)), + satisfies( + ErrorAttributes.ERROR_TYPE, + val -> + val.satisfiesAnyOf( + v -> assertThat(spanData.getExceptionClass()).isNull(), + v -> assertThat(v).isEqualTo("500")))); + } + + SpanDataAssert assertCompileSpan(SpanDataAssert span, JspSpan spanData) { + if (spanData.getExceptionClass() != null) { + span.hasStatus(StatusData.error()) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfyingExactly( + equalTo( + ExceptionAttributes.EXCEPTION_TYPE, + spanData.getExceptionClass().getCanonicalName()), + satisfies( + ExceptionAttributes.EXCEPTION_STACKTRACE, + val -> val.isInstanceOf(String.class)), + satisfies( + ExceptionAttributes.EXCEPTION_MESSAGE, + val -> val.isInstanceOf(String.class)))); + } + + return span.hasName("Compile " + spanData.getRoute()) + .hasParent(spanData.getParent()) + .hasAttributesSatisfyingExactly( + equalTo(stringKey("jsp.classFQCN"), "org.apache.jsp." + spanData.getClassName()), + equalTo(stringKey("jsp.compiler"), "org.apache.jasper.compiler.JDTCompiler")); + } + + SpanDataAssert assertRenderSpan(SpanDataAssert span, JspSpan spanData) { + String requestUrl = spanData.getRoute(); + if (spanData.getRequestUrlOverride() != null) { + requestUrl = spanData.getRequestUrlOverride(); + } + + if (spanData.getExceptionClass() != null) { + span.hasStatus(StatusData.error()) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfyingExactly( + satisfies( + ExceptionAttributes.EXCEPTION_TYPE, + val -> + val.satisfiesAnyOf( + v -> val.isEqualTo(spanData.getExceptionClass().getName()), + v -> + val.contains( + spanData.getExceptionClass().getSimpleName()))), + satisfies( + ExceptionAttributes.EXCEPTION_MESSAGE, + val -> + val.satisfiesAnyOf( + v -> assertThat(spanData.getErrorMessageOptional()).isTrue(), + v -> val.isInstanceOf(String.class))), + satisfies( + ExceptionAttributes.EXCEPTION_STACKTRACE, + val -> val.isInstanceOf(String.class)))); + } + + return span.hasName("Render " + spanData.getRoute()) + .hasParent(spanData.getParent()) + .hasAttributesSatisfyingExactly( + equalTo(stringKey("jsp.requestURL"), baseUrl + requestUrl), + satisfies( + stringKey("jsp.forwardOrigin"), + val -> + val.satisfiesAnyOf( + v -> assertThat(spanData.getForwardOrigin()).isNull(), + v -> assertThat(v).isEqualTo(spanData.getForwardOrigin())))); + } +}