Skip to content

Commit 803550c

Browse files
Added Instrumentation for ServletContextListener#contextInitialized (#2443)
1 parent 028f562 commit 803550c

File tree

7 files changed

+73
-28
lines changed

7 files changed

+73
-28
lines changed

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentation.java

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
* <li>{@link jakarta.servlet.Filter#init(jakarta.servlet.FilterConfig)}</li>
4646
* <li>{@link javax.servlet.Servlet#init(javax.servlet.ServletConfig)}</li>
4747
* <li>{@link jakarta.servlet.Servlet#init(jakarta.servlet.ServletConfig)}</li>
48+
* <li>{@link javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)}</li>
49+
* <li>{@link jakarta.servlet.ServletContextListener#contextInitialized(jakarta.servlet.ServletContextEvent)}</li>
4850
* </ul>
4951
*
5052
* Determines the service name based on the webapp's {@code META-INF/MANIFEST.MF} file early in the startup process.
@@ -54,19 +56,24 @@ public abstract class InitServiceNameInstrumentation extends AbstractServletInst
5456

5557
@Override
5658
public ElementMatcher<? super NamedElement> getTypeMatcherPreFilter() {
57-
return nameContains("Filter").or(nameContains("Servlet"));
59+
return nameContains("Filter").or(nameContains("Servlet")).or(nameContains("Listener"));
5860
}
5961

6062
@Override
6163
public ElementMatcher<? super TypeDescription> getTypeMatcher() {
62-
return not(isInterface()).and(hasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.Servlet", "jakarta.servlet.Filter", "jakarta.servlet.Servlet")));
64+
return not(isInterface()).and(hasSuperType(namedOneOf(
65+
"javax.servlet.ServletContextListener", "javax.servlet.Filter", "javax.servlet.Servlet",
66+
"jakarta.servlet.ServletContextListener", "jakarta.servlet.Filter", "jakarta.servlet.Servlet")));
6367
}
6468

6569
@Override
6670
public ElementMatcher<? super MethodDescription> getMethodMatcher() {
6771
return named("init")
6872
.and(takesArguments(1))
69-
.and(takesArgument(0, nameEndsWith("Config")));
73+
.and(takesArgument(0, nameEndsWith("Config")))
74+
.or(named("contextInitialized")
75+
.and(takesArguments(1))
76+
.and(takesArgument(0, nameEndsWith("ServletContextEvent"))));
7077
}
7178

7279
public static class JavaxInitServiceNameInstrumentation extends InitServiceNameInstrumentation {
@@ -80,15 +87,14 @@ public String rootClassNameThatClassloaderCanLoad() {
8087

8188
public static class AdviceClass {
8289
@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
83-
public static void onEnter(@Advice.Argument(0) @Nullable Object config) {
84-
if (config == null) {
85-
return;
86-
}
90+
public static void onEnter(@Advice.Argument(0) @Nullable Object arg) {
8791
javax.servlet.ServletContext servletContext;
88-
if (config instanceof javax.servlet.FilterConfig) {
89-
servletContext = adapter.getServletContextFromFilterConfig((javax.servlet.FilterConfig) config);
90-
} else if (config instanceof javax.servlet.ServletConfig) {
91-
servletContext = adapter.getServletContextFromServletConfig((javax.servlet.ServletConfig) config);
92+
if (arg instanceof javax.servlet.FilterConfig) {
93+
servletContext = adapter.getServletContextFromFilterConfig((javax.servlet.FilterConfig) arg);
94+
} else if (arg instanceof javax.servlet.ServletConfig) {
95+
servletContext = adapter.getServletContextFromServletConfig((javax.servlet.ServletConfig) arg);
96+
} else if (arg instanceof javax.servlet.ServletContextEvent) {
97+
servletContext = adapter.getServletContextFromServletContextEvent((javax.servlet.ServletContextEvent) arg);
9298
} else {
9399
return;
94100
}
@@ -109,15 +115,14 @@ public String rootClassNameThatClassloaderCanLoad() {
109115
public static class AdviceClass {
110116

111117
@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
112-
public static void onEnter(@Advice.Argument(0) @Nullable Object config) {
113-
if (config == null) {
114-
return;
115-
}
118+
public static void onEnter(@Advice.Argument(0) @Nullable Object arg) {
116119
jakarta.servlet.ServletContext servletContext;
117-
if (config instanceof jakarta.servlet.FilterConfig) {
118-
servletContext = adapter.getServletContextFromFilterConfig((jakarta.servlet.FilterConfig) config);
119-
} else if (config instanceof jakarta.servlet.ServletConfig) {
120-
servletContext = adapter.getServletContextFromServletConfig((jakarta.servlet.ServletConfig) config);
120+
if (arg instanceof jakarta.servlet.FilterConfig) {
121+
servletContext = adapter.getServletContextFromFilterConfig((jakarta.servlet.FilterConfig) arg);
122+
} else if (arg instanceof jakarta.servlet.ServletConfig) {
123+
servletContext = adapter.getServletContextFromServletConfig((jakarta.servlet.ServletConfig) arg);
124+
} else if (arg instanceof jakarta.servlet.ServletContextEvent) {
125+
servletContext = adapter.getServletContextFromServletContextEvent((jakarta.servlet.ServletContextEvent) arg);
121126
} else {
122127
return;
123128
}

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ public abstract class ServletApiAdvice {
5858
private static final List<String> requestExceptionAttributes = Arrays.asList("javax.servlet.error.exception", "jakarta.servlet.error.exception", "exception", "org.springframework.web.servlet.DispatcherServlet.EXCEPTION", "co.elastic.apm.exception");
5959

6060
@Nullable
61-
public static <HttpServletRequest, HttpServletResponse, ServletContext, FilterConfig, ServletConfig> Object onServletEnter(
62-
ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, FilterConfig, ServletConfig> adapter,
61+
public static <HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> Object onServletEnter(
62+
ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> adapter,
6363
Object servletRequest) {
6464

6565
ElasticApmTracer tracer = GlobalTracer.getTracerImpl();
@@ -159,8 +159,8 @@ public static <HttpServletRequest, HttpServletResponse, ServletContext, FilterCo
159159
return ret;
160160
}
161161

162-
public static <HttpServletRequest, HttpServletResponse, ServletContext, FilterConfig, ServletConfig> void onExitServlet(
163-
ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, FilterConfig, ServletConfig> adapter,
162+
public static <HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> void onExitServlet(
163+
ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> adapter,
164164
Object servletRequest,
165165
Object servletResponse,
166166
@Nullable Object transactionOrScopeOrSpan,

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import jakarta.servlet.RequestDispatcher;
2727
import jakarta.servlet.ServletConfig;
2828
import jakarta.servlet.ServletContext;
29+
import jakarta.servlet.ServletContextEvent;
2930
import jakarta.servlet.http.Cookie;
3031
import jakarta.servlet.http.HttpServlet;
3132
import jakarta.servlet.http.HttpServletRequest;
@@ -38,7 +39,7 @@
3839
import java.util.Enumeration;
3940
import java.util.Map;
4041

41-
public class JakartaServletApiAdapter implements ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, FilterConfig, ServletConfig> {
42+
public class JakartaServletApiAdapter implements ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> {
4243

4344
public static final JakartaServletApiAdapter INSTANCE = new JakartaServletApiAdapter();
4445

@@ -214,6 +215,11 @@ public boolean isInstanceOfHttpServlet(Object object) {
214215
return object instanceof HttpServlet;
215216
}
216217

218+
@Override
219+
public ServletContext getServletContextFromServletContextEvent(ServletContextEvent servletContextEvent) {
220+
return servletContextEvent.getServletContext();
221+
}
222+
217223
@Override
218224
public ServletContext getServletContextFromServletConfig(ServletConfig filterConfig) {
219225
return filterConfig.getServletContext();

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JavaxServletApiAdapter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import javax.servlet.RequestDispatcher;
2929
import javax.servlet.ServletConfig;
3030
import javax.servlet.ServletContext;
31+
import javax.servlet.ServletContextEvent;
3132
import javax.servlet.http.Cookie;
3233
import javax.servlet.http.HttpServlet;
3334
import javax.servlet.http.HttpServletRequest;
@@ -38,7 +39,7 @@
3839
import java.util.Enumeration;
3940
import java.util.Map;
4041

41-
public class JavaxServletApiAdapter implements ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, FilterConfig, ServletConfig> {
42+
public class JavaxServletApiAdapter implements ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> {
4243

4344
private static final JavaxServletApiAdapter INSTANCE = new JavaxServletApiAdapter();
4445

@@ -213,6 +214,11 @@ public boolean isInstanceOfHttpServlet(Object object) {
213214
return object instanceof HttpServlet;
214215
}
215216

217+
@Override
218+
public ServletContext getServletContextFromServletContextEvent(ServletContextEvent servletContextEvent) {
219+
return servletContextEvent.getServletContext();
220+
}
221+
216222
@Override
217223
public ServletContext getServletContextFromServletConfig(ServletConfig filterConfig) {
218224
return filterConfig.getServletContext();

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletAdapter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
import co.elastic.apm.agent.sdk.state.GlobalState;
2222

2323
@GlobalState
24-
public interface ServletAdapter<ServletConfig, ServletContext> {
24+
public interface ServletAdapter<ServletContextEvent, ServletConfig, ServletContext> {
2525

2626
boolean isInstanceOfHttpServlet(Object object);
2727

28+
ServletContext getServletContextFromServletContextEvent(ServletContextEvent servletContextEvent);
29+
2830
ServletContext getServletContextFromServletConfig(ServletConfig filterConfig);
2931

3032
}

apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/ServletApiAdapter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
import co.elastic.apm.agent.sdk.state.GlobalState;
2222

2323
@GlobalState
24-
public interface ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, FilterConfig, ServletConfig> extends
24+
public interface ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> extends
2525
ServletRequestResponseAdapter<HttpServletRequest, HttpServletResponse, ServletContext>,
2626
ServletContextAdapter<ServletContext>,
27-
ServletAdapter<ServletConfig, ServletContext>,
27+
ServletAdapter<ServletContextEvent, ServletConfig, ServletContext>,
2828
FilterAdapter<FilterConfig, ServletContext> {
2929

3030
}

apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/InitServiceNameInstrumentationTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@
2323
import org.junit.jupiter.api.Test;
2424
import org.springframework.mock.web.MockFilterConfig;
2525
import org.springframework.mock.web.MockServletConfig;
26+
import org.springframework.mock.web.MockServletContext;
2627

2728
import javax.servlet.Filter;
2829
import javax.servlet.FilterChain;
2930
import javax.servlet.FilterConfig;
3031
import javax.servlet.Servlet;
32+
import javax.servlet.ServletContextEvent;
33+
import javax.servlet.ServletContextListener;
3134
import javax.servlet.ServletException;
3235
import javax.servlet.ServletRequest;
3336
import javax.servlet.ServletResponse;
@@ -38,6 +41,19 @@
3841

3942
class InitServiceNameInstrumentationTest extends AbstractInstrumentationTest {
4043

44+
@Test
45+
void testContextInitialized() {
46+
ServletContextListener servletContextListener = new NoopServletContextListener();
47+
48+
CustomManifestLoader cl = new CustomManifestLoader(() -> getClass().getResourceAsStream("/TEST-MANIFEST.MF"));
49+
CustomManifestLoader.withThreadContextClassLoader(cl, () -> {
50+
servletContextListener.contextInitialized(new ServletContextEvent(new MockServletContext()));
51+
tracer.startRootTransaction(cl).end();
52+
});
53+
54+
assertServiceInfo();
55+
}
56+
4157
@Test
4258
void testServletInit() {
4359
Servlet servlet = new HttpServlet() {
@@ -71,6 +87,16 @@ private void assertServiceInfo() {
7187
assertThat(traceContext.getServiceVersion()).isEqualTo("1.42.0");
7288
}
7389

90+
private static class NoopServletContextListener implements ServletContextListener {
91+
@Override
92+
public void contextInitialized(ServletContextEvent servletContextEvent) {
93+
}
94+
95+
@Override
96+
public void contextDestroyed(ServletContextEvent servletContextEvent) {
97+
}
98+
}
99+
74100
private static class NoopFilter implements Filter {
75101
@Override
76102
public void init(FilterConfig filterConfig) {

0 commit comments

Comments
 (0)