apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/JakartaApmAsyncListener.java [71:181]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        this.transaction = transaction;
        return this;
    }

    @Override
    public void onComplete(AsyncEvent event) {
        endTransaction(event);
    }

    @Override
    public void onTimeout(AsyncEvent event) {
        throwable = asyncContextAdviceHelperImpl.getTracer().redactExceptionIfRequired(event.getThrowable());
        if (isJBossEap6(event)) {
            endTransaction(event);
        }
        /*
            NOTE: HTTP status code may not have been set yet, so we do not call endTransaction() from here.

            According to the Servlet 3 specification
            (http://download.oracle.com/otn-pub/jcp/servlet-3.0-fr-eval-oth-JSpec/servlet-3_0-final-spec.pdf, section 2.3.3.3),
            onComplete() should always be called by the container even in the case of timeout or error, and the final
            HTTP status code should be set by then. So we'll just defer to onComplete() for finalizing the span and do
            nothing here.

            But JBoss EAP 6 is a special one...
        */
    }

    @Override
    public void onError(AsyncEvent event) {
        throwable = asyncContextAdviceHelperImpl.getTracer().redactExceptionIfRequired(event.getThrowable());
        if (isJBossEap6(event)) {
            endTransaction(event);
        }
        /*
            NOTE: HTTP status code may not have been set yet, so we only hold a reference to the related error that may not be
            otherwise available, but not calling endTransaction() from here.

            According to the Servlet 3 specification
            (http://download.oracle.com/otn-pub/jcp/servlet-3.0-fr-eval-oth-JSpec/servlet-3_0-final-spec.pdf, section 2.3.3.3),
            onComplete() should always be called by the container even in the case of timeout or error, and the final
            HTTP status code should be set by then. So we'll just defer to onComplete() for finalizing the span and do
            nothing here.

            But JBoss EAP 6 is a special one...
        */
    }

    private boolean isJBossEap6(AsyncEvent event) {
        final ServletContext context = event.getSuppliedRequest().getServletContext();
        return context.getMajorVersion() == 3 && context.getMinorVersion() == 0 && System.getProperty("jboss.home.dir") != null;
    }

    /**
     * If another async is created (ex via asyncContext.dispatch), this needs to be re-attached
     */
    @Override
    public void onStartAsync(AsyncEvent event) {
        AsyncContext eventAsyncContext = event.getAsyncContext();
        if (eventAsyncContext != null) {
            eventAsyncContext.addListener(this, event.getSuppliedRequest(), event.getSuppliedResponse());
        }
    }

    // unfortunately, the duplication can't be avoided,
    // because only the onExitServletService method may contain references to the servlet API
    // (see class-level Javadoc)
    private void endTransaction(AsyncEvent event) {
        // To ensure transaction is ended only by a single event
        if (completed.getAndSet(true) || transaction == null) {
            return;
        }

        try {
            HttpServletRequest request = (HttpServletRequest) event.getSuppliedRequest();
            request.removeAttribute(TRANSACTION_ATTRIBUTE);

            HttpServletResponse response = (HttpServletResponse) event.getSuppliedResponse();
            final Response resp = transaction.getContext().getResponse();
            if (transaction.isSampled() && servletTransactionHelper.isCaptureHeaders()) {
                for (String headerName : response.getHeaderNames()) {
                    resp.addHeader(headerName, response.getHeaders(headerName));
                }
            }
            // request.getParameterMap() may allocate a new map, depending on the servlet container implementation
            // so only call this method if necessary
            final String contentTypeHeader = request.getHeader("Content-Type");
            final Map<String, String[]> parameterMap;
            if (transaction.isSampled() && servletTransactionHelper.captureParameters(request.getMethod(), contentTypeHeader)) {
                parameterMap = request.getParameterMap();
            } else {
                parameterMap = null;
            }
            Throwable throwableToSend = event.getThrowable();
            if (throwableToSend == null) {
                throwableToSend = throwable;
            }
            servletTransactionHelper.onAfter(transaction, throwableToSend,
                response.isCommitted(), response.getStatus(), true, request.getMethod(), parameterMap,
                request.getServletPath(), request.getPathInfo(), contentTypeHeader, false
            );
        } finally {
            asyncContextAdviceHelperImpl.recycle(this);
        }
    }

    @Override
    public void resetState() {
        transaction = null;
        throwable = null;
        completed.set(false);
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/JavaxApmAsyncListener.java [70:180]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        this.transaction = transaction;
        return this;
    }

    @Override
    public void onComplete(AsyncEvent event) {
        endTransaction(event);
    }

    @Override
    public void onTimeout(AsyncEvent event) {
        throwable = asyncContextAdviceHelperImpl.getTracer().redactExceptionIfRequired(event.getThrowable());
        if (isJBossEap6(event)) {
            endTransaction(event);
        }
        /*
            NOTE: HTTP status code may not have been set yet, so we do not call endTransaction() from here.

            According to the Servlet 3 specification
            (http://download.oracle.com/otn-pub/jcp/servlet-3.0-fr-eval-oth-JSpec/servlet-3_0-final-spec.pdf, section 2.3.3.3),
            onComplete() should always be called by the container even in the case of timeout or error, and the final
            HTTP status code should be set by then. So we'll just defer to onComplete() for finalizing the span and do
            nothing here.

            But JBoss EAP 6 is a special one...
        */
    }

    @Override
    public void onError(AsyncEvent event) {
        throwable = asyncContextAdviceHelperImpl.getTracer().redactExceptionIfRequired(event.getThrowable());
        if (isJBossEap6(event)) {
            endTransaction(event);
        }
        /*
            NOTE: HTTP status code may not have been set yet, so we only hold a reference to the related error that may not be
            otherwise available, but not calling endTransaction() from here.

            According to the Servlet 3 specification
            (http://download.oracle.com/otn-pub/jcp/servlet-3.0-fr-eval-oth-JSpec/servlet-3_0-final-spec.pdf, section 2.3.3.3),
            onComplete() should always be called by the container even in the case of timeout or error, and the final
            HTTP status code should be set by then. So we'll just defer to onComplete() for finalizing the span and do
            nothing here.

            But JBoss EAP 6 is a special one...
        */
    }

    private boolean isJBossEap6(AsyncEvent event) {
        final ServletContext context = event.getSuppliedRequest().getServletContext();
        return context.getMajorVersion() == 3 && context.getMinorVersion() == 0 && System.getProperty("jboss.home.dir") != null;
    }

    /**
     * If another async is created (ex via asyncContext.dispatch), this needs to be re-attached
     */
    @Override
    public void onStartAsync(AsyncEvent event) {
        AsyncContext eventAsyncContext = event.getAsyncContext();
        if (eventAsyncContext != null) {
            eventAsyncContext.addListener(this, event.getSuppliedRequest(), event.getSuppliedResponse());
        }
    }

    // unfortunately, the duplication can't be avoided,
    // because only the onExitServletService method may contain references to the servlet API
    // (see class-level Javadoc)
    private void endTransaction(AsyncEvent event) {
        // To ensure transaction is ended only by a single event
        if (completed.getAndSet(true) || transaction == null) {
            return;
        }

        try {
            HttpServletRequest request = (HttpServletRequest) event.getSuppliedRequest();
            request.removeAttribute(TRANSACTION_ATTRIBUTE);

            HttpServletResponse response = (HttpServletResponse) event.getSuppliedResponse();
            final Response resp = transaction.getContext().getResponse();
            if (transaction.isSampled() && servletTransactionHelper.isCaptureHeaders()) {
                for (String headerName : response.getHeaderNames()) {
                    resp.addHeader(headerName, response.getHeaders(headerName));
                }
            }
            // request.getParameterMap() may allocate a new map, depending on the servlet container implementation
            // so only call this method if necessary
            final String contentTypeHeader = request.getHeader("Content-Type");
            final Map<String, String[]> parameterMap;
            if (transaction.isSampled() && servletTransactionHelper.captureParameters(request.getMethod(), contentTypeHeader)) {
                parameterMap = request.getParameterMap();
            } else {
                parameterMap = null;
            }
            Throwable throwableToSend = event.getThrowable();
            if (throwableToSend == null) {
                throwableToSend = throwable;
            }
            servletTransactionHelper.onAfter(transaction, throwableToSend,
                response.isCommitted(), response.getStatus(), true, request.getMethod(), parameterMap,
                request.getServletPath(), request.getPathInfo(), contentTypeHeader, false
            );
        } finally {
            asyncContextAdviceHelperImpl.recycle(this);
        }
    }

    @Override
    public void resetState() {
        transaction = null;
        throwable = null;
        completed.set(false);
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



