Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions metrics-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@
<artifactId>metrics-jetty12-ee10</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jetty12-ee11</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jmx</artifactId>
Expand Down
145 changes: 145 additions & 0 deletions metrics-jetty12-ee11/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-parent</artifactId>
<version>4.2.34-SNAPSHOT</version>
</parent>

<artifactId>metrics-jetty12-ee11</artifactId>
<name>Metrics Integration for Jetty 12.x and higher with Jakarta EE 11</name>
<packaging>bundle</packaging>
<description>
A set of extensions for Jetty 12.x and higher which provide instrumentation of thread pools, connector
metrics, and application latency and utilization. This module uses the Servlet API from Jakarta EE 11.
</description>

<properties>
<javaModuleName>io.dropwizard.metrics.jetty12.ee11</javaModuleName>

<maven.compiler.release>17</maven.compiler.release>

<slf4j.version>2.0.17</slf4j.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-bom</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-bom</artifactId>
<version>${jetty12.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-bom</artifactId>
<version>${jetty12.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${byte-buddy.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>${servlet6.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-annotation</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jetty12</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-servlet</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>${awaitility.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package io.dropwizard.metrics.jetty12.ee11;

import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.annotation.ResponseMeteredLevel;
import io.dropwizard.metrics.jetty12.AbstractInstrumentedHandler;
import jakarta.servlet.AsyncEvent;
import jakarta.servlet.AsyncListener;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import org.eclipse.jetty.ee11.servlet.ServletApiRequest;
import org.eclipse.jetty.ee11.servlet.ServletChannelState;
import org.eclipse.jetty.ee11.servlet.ServletContextHandler;
import org.eclipse.jetty.ee11.servlet.ServletContextRequest;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;

import java.io.IOException;

/**
* A Jetty {@link Handler} which records various metrics about an underlying {@link Handler}
* instance. This {@link Handler} requires a {@link org.eclipse.jetty.ee11.servlet.ServletContextHandler} to be present.
* For correct behaviour, the {@link org.eclipse.jetty.ee11.servlet.ServletContextHandler} should be before this handler
* in the handler chain. To achieve this, one can use
* {@link org.eclipse.jetty.ee11.servlet.ServletContextHandler#insertHandler(Singleton)}.
*/
public class InstrumentedEE11Handler extends AbstractInstrumentedHandler {
private AsyncDispatchesAwareServletRequestListener asyncDispatchesAwareServletRequestListener;

/**
* Create a new instrumented handler using a given metrics registry.
*
* @param registry the registry for the metrics
*/
public InstrumentedEE11Handler(MetricRegistry registry) {
super(registry);
}

/**
* Create a new instrumented handler using a given metrics registry.
*
* @param registry the registry for the metrics
* @param prefix the prefix to use for the metrics names
*/
public InstrumentedEE11Handler(MetricRegistry registry, String prefix) {
super(registry, prefix);
}

/**
* Create a new instrumented handler using a given metrics registry.
*
* @param registry the registry for the metrics
* @param prefix the prefix to use for the metrics names
* @param responseMeteredLevel the level to determine individual/aggregate response codes that are instrumented
*/
public InstrumentedEE11Handler(MetricRegistry registry, String prefix, ResponseMeteredLevel responseMeteredLevel) {
super(registry, prefix, responseMeteredLevel);
}

@Override
protected void doStart() throws Exception {
super.doStart();
asyncDispatchesAwareServletRequestListener = new AsyncDispatchesAwareServletRequestListener(getAsyncDispatches());
}

@Override
protected void doStop() throws Exception {
super.doStop();
}

@Override
protected void setupServletListeners(Request request, Response response) {
ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class);
if (servletContextRequest == null) {
return;
}

ServletChannelState servletChannelState = servletContextRequest.getServletRequestState();
// the ServletChannelState gets recycled after handling, so add a new listener for every request
servletChannelState.addListener(new InstrumentedAsyncListener(getAsyncTimeouts()));

ServletContextHandler servletContextHandler = servletContextRequest.getServletContextHandler();
// addEventListener checks for duplicates, so we can try to add the listener for every request
servletContextHandler.addEventListener(asyncDispatchesAwareServletRequestListener);
}

@Override
protected boolean isSuspended(Request request, Response response) {
ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class);
if (servletContextRequest == null) {
return false;
}

ServletChannelState servletChannelState = servletContextRequest.getServletRequestState();
if (servletChannelState == null) {
return false;
}

return servletChannelState.isSuspended();
}

private static class AsyncDispatchesAwareServletRequestListener implements ServletRequestListener {
private final Meter asyncDispatches;

private AsyncDispatchesAwareServletRequestListener(Meter asyncDispatches) {
this.asyncDispatches = asyncDispatches;
}

@Override
public void requestInitialized(ServletRequestEvent sre) {
ServletRequest servletRequest = sre.getServletRequest();
if (!(servletRequest instanceof ServletApiRequest)) {
return;
}

ServletApiRequest servletApiRequest = (ServletApiRequest) servletRequest;

ServletContextHandler.ServletRequestInfo servletRequestInfo = servletApiRequest.getServletRequestInfo();

ServletChannelState servletChannelState = servletRequestInfo.getServletRequestState();

// if the request isn't 'initial', the request was re-dispatched
if (servletChannelState.isAsync() && !servletChannelState.isInitial()) {
asyncDispatches.mark();
}
}
}

private static class InstrumentedAsyncListener implements AsyncListener {
private final Meter asyncTimeouts;

private InstrumentedAsyncListener(Meter asyncTimeouts) {
this.asyncTimeouts = asyncTimeouts;
}

@Override
public void onComplete(AsyncEvent event) throws IOException {}

@Override
public void onTimeout(AsyncEvent event) throws IOException {
asyncTimeouts.mark();
}

@Override
public void onError(AsyncEvent event) throws IOException {}

@Override
public void onStartAsync(AsyncEvent event) throws IOException {
event.getAsyncContext().addListener(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.dropwizard.metrics.jetty12.ee11;

import com.codahale.metrics.MetricRegistry;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.ee11.servlet.ServletContextHandler;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.junit.After;
import org.junit.Before;

import static com.codahale.metrics.annotation.ResponseMeteredLevel.ALL;

abstract class AbstractIntegrationTest {

protected final HttpClient client = new HttpClient();
protected final MetricRegistry registry = new MetricRegistry();
protected final Server server = new Server();
protected final ServerConnector connector = new ServerConnector(server);
protected final InstrumentedEE11Handler handler = new InstrumentedEE11Handler(registry, null, ALL);
protected final ServletContextHandler servletContextHandler = new ServletContextHandler();

@Before
public void setUp() throws Exception {
handler.setName("handler");

// builds the following handler chain:
// ServletContextHandler -> InstrumentedHandler -> TestHandler
// the ServletContextHandler is needed to utilize servlet related classes
servletContextHandler.setHandler(getHandler());
servletContextHandler.insertHandler(handler);
server.setHandler(servletContextHandler);

server.addConnector(connector);
server.start();
client.start();
}

@After
public void tearDown() throws Exception {
server.stop();
client.stop();
}

protected String uri(String path) {
return "http://localhost:" + connector.getLocalPort() + path;
}

protected abstract Handler getHandler();
}
Loading
Loading