diff --git a/README.md b/README.md index b9433b33..4db00548 100644 --- a/README.md +++ b/README.md @@ -343,8 +343,8 @@ In a Servlet environment, it might be easier to use the `ServletMatomoRequest` c import jakarta.servlet.http.HttpServletRequest; import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.parameters.DeviceResolution; -import org.matomo.java.tracking.ServletMatomoRequest; +import org.matomo.java.tracking.servlet.JakartaHttpServletWrapper; +import org.matomo.java.tracking.servlet.ServletMatomoRequest; public class ServletMatomoRequestExample { @@ -353,10 +353,10 @@ public class ServletMatomoRequestExample { public ServletMatomoRequestExample(MatomoTracker tracker) { this.tracker = tracker; } - + public void someControllerMethod(HttpServletRequest req) { MatomoRequest matomoRequest = ServletMatomoRequest - .fromServletRequest(req) + .fromServletRequest(JakartaHttpServletWrapper.fromHttpServletRequest(req)) .actionName("Some Controller Action") // ... .build(); @@ -369,7 +369,8 @@ public class ServletMatomoRequestExample { The `ServletMatomoRequest` automatically sets the action URL, applies browser request headers, corresponding Matomo cookies and the visitor IP address. It sets the visitor ID, Matomo session ID, custom variables and heatmap -if Matomo cookies are present. +if Matomo cookies are present. Since there was a renaming from Java EE (javax) to Jakarta EE (jakarta), we provide a +wrapper class `JakartaHttpServletWrapper` for Jakarta and `JavaxHttpServletWrapper` for javax. ### Tracking Configuration diff --git a/core/src/main/java/org/matomo/java/tracking/servlet/CookieWrapper.java b/core/src/main/java/org/matomo/java/tracking/servlet/CookieWrapper.java new file mode 100644 index 00000000..4c29ae3d --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/servlet/CookieWrapper.java @@ -0,0 +1,15 @@ +package org.matomo.java.tracking.servlet; + +import lombok.Value; + +/** + * Wrapper for the cookie name and value. + */ +@Value +public class CookieWrapper { + + String name; + + String value; + +} diff --git a/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java b/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java new file mode 100644 index 00000000..51dfa0d3 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/servlet/HttpServletRequestWrapper.java @@ -0,0 +1,53 @@ +package org.matomo.java.tracking.servlet; + +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +/** + * Wraps a HttpServletRequest to be compatible with both the Jakarta and the Java EE API. + */ +@Builder +@Value +public class HttpServletRequestWrapper { + + StringBuffer requestURL; + + String remoteAddr; + + Map headers; + + CookieWrapper[] cookies; + + /** + * Returns an enumeration of all the header names this request contains. If the request has no + * headers, this method returns an empty enumeration. + * + * @return an enumeration of all the header names sent with this request + */ + public Enumeration getHeaderNames() { + return headers == null ? Collections.emptyEnumeration() : + Collections.enumeration(headers.keySet()); + } + + /** + * Returns the value of the specified request header as a String. If the request did not include a + * header of the specified name, this method returns null. If there are multiple headers with the + * same name, this method returns the last header in the request. The header name is case + * insensitive. You can use this method with any request header. + * + * @param name a String specifying the header name (case insensitive) - must not be {@code null}. + * @return a String containing the value of the requested header, or null if the request does not + * have a header of that name + */ + @Nullable + public String getHeader(@NonNull String name) { + return headers == null ? null : headers.get(name.toLowerCase(Locale.ROOT)); + } + +} diff --git a/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java similarity index 89% rename from servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java rename to core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java index 5d8be191..6b2125d3 100644 --- a/servlet/src/main/java/org/matomo/java/tracking/ServletMatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/servlet/ServletMatomoRequest.java @@ -1,10 +1,8 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.servlet; import static java.util.Arrays.asList; import edu.umd.cs.findbugs.annotations.Nullable; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -14,16 +12,17 @@ import java.util.Map; import java.util.Set; import lombok.NonNull; +import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.parameters.CustomVariables; import org.matomo.java.tracking.parameters.VisitorId; /** - * Adds the headers from a {@link HttpServletRequest} to a + * Adds the headers from a {@link HttpServletRequestWrapper} to a * {@link MatomoRequest.MatomoRequestBuilder}. * - *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from the - * request or #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to + *

Use #fromServletRequest(HttpServletRequestWrapper) to create a new builder with the headers from the + * request or #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequestWrapper) to * add the headers to an existing builder. */ public final class ServletMatomoRequest { @@ -47,7 +46,7 @@ private ServletMatomoRequest() { /** * Creates a new builder with the headers from the request. * - *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequest) to + *

Use #addServletRequestHeaders(MatomoRequest.MatomoRequestBuilder, HttpServletRequestWrapper) to * add the headers to an existing builder. * * @param request the request to get the headers from (must not be null) @@ -55,14 +54,14 @@ private ServletMatomoRequest() { * @return a new builder with the headers from the request (never null) */ @edu.umd.cs.findbugs.annotations.NonNull - public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull HttpServletRequest request) { + public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull HttpServletRequestWrapper request) { return addServletRequestHeaders(MatomoRequest.request(), request); } /** * Adds the headers from the request to an existing builder. * - *

Use #fromServletRequest(HttpServletRequest) to create a new builder with the headers from + *

Use #fromServletRequest(HttpServletRequestWrapper) to create a new builder with the headers from * the request. * * @param builder the builder to add the headers to (must not be null) @@ -72,7 +71,7 @@ public static MatomoRequest.MatomoRequestBuilder fromServletRequest(@NonNull Htt */ @edu.umd.cs.findbugs.annotations.NonNull public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( - @NonNull MatomoRequest.MatomoRequestBuilder builder, @NonNull HttpServletRequest request + @NonNull MatomoRequest.MatomoRequestBuilder builder, @NonNull HttpServletRequestWrapper request ) { return builder .actionUrl(request.getRequestURL() == null ? null : request.getRequestURL().toString()) @@ -83,7 +82,7 @@ public static MatomoRequest.MatomoRequestBuilder addServletRequestHeaders( @edu.umd.cs.findbugs.annotations.NonNull private static Map collectHeaders( - @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequestWrapper request ) { Map headers = new HashMap<>(10); Enumeration headerNames = request.getHeaderNames(); @@ -100,7 +99,7 @@ private static Map collectHeaders( @Nullable private static String determineVisitorIp( - @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequestWrapper request ) { String realIpHeader = request.getHeader("X-Real-Ip"); if (isNotEmpty(realIpHeader)) { @@ -119,12 +118,12 @@ private static String determineVisitorIp( @edu.umd.cs.findbugs.annotations.NonNull private static Map processCookies( @edu.umd.cs.findbugs.annotations.NonNull MatomoRequest.MatomoRequestBuilder builder, - @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequest request + @edu.umd.cs.findbugs.annotations.NonNull HttpServletRequestWrapper request ) { Map cookies = new LinkedHashMap<>(3); if (request.getCookies() != null) { builder.supportsCookies(Boolean.TRUE); - for (Cookie cookie : request.getCookies()) { + for (CookieWrapper cookie : request.getCookies()) { if (isNotEmpty(cookie.getValue())) { processCookie(builder, cookies, cookie.getName(), cookie.getValue()); } diff --git a/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java b/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java similarity index 62% rename from servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java rename to core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java index a79cda66..65eabd2c 100644 --- a/servlet/src/test/java/org/matomo/java/tracking/ServletMatomoRequestTest.java +++ b/core/src/test/java/org/matomo/java/tracking/servlet/ServletMatomoRequestTest.java @@ -1,34 +1,37 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.servlet; -import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.matomo.java.tracking.MatomoRequest; class ServletMatomoRequestTest { @Test void addsServletRequestHeaders() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap("headerName", "headerValue")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap("headername", "headerValue")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); MatomoRequest matomoRequest = builder.build(); - assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headerName", "headerValue"); + assertThat(matomoRequest.getHeaders()).hasSize(1).containsEntry("headername", "headerValue"); } @Test void skipsEmptyHeaderNames() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap("", "headerValue")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap("", "headerValue")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -40,8 +43,10 @@ void skipsEmptyHeaderNames() { @Test void skipsBlankHeaderNames() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap(" ", "headerValue")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap(" ", "headerValue")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -53,8 +58,10 @@ void skipsBlankHeaderNames() { @ParameterizedTest @ValueSource(strings = {"connection", "content-length", "expect", "host", "upgrade"}) void doesNotAddRestrictedHeaders(String restrictedHeader) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap(restrictedHeader, "headerValue")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap(restrictedHeader, "headerValue")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -73,7 +80,7 @@ void failsIfServletRequestIsNull() { void failsIfBuilderIsNull() { assertThatThrownBy(() -> ServletMatomoRequest.addServletRequestHeaders( null, - new MockHttpServletRequest() + HttpServletRequestWrapper.builder().build() )) .isInstanceOf(NullPointerException.class) .hasMessage("builder is marked non-null but is null"); @@ -81,8 +88,12 @@ void failsIfBuilderIsNull() { @Test void extractsVisitorIdFromCookie() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(singleton(new Cookie("_pk_id.1.1fff", "be40d677d6c7270b.1699801331."))); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .cookies(new CookieWrapper[] { + new CookieWrapper("_pk_id.1.1fff", "be40d677d6c7270b.1699801331.") + }) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -98,8 +109,10 @@ void extractsVisitorIdFromCookie() { strings = {"_pk_ses.1.1fff", "_pk_ref.1.1fff", "_pk_hsr.1.1fff"} ) void extractsMatomoCookies(String cookieName) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(singleton(new Cookie(cookieName, "anything"))); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .cookies(new CookieWrapper[] {new CookieWrapper(cookieName, "anything")}) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -109,8 +122,15 @@ void extractsMatomoCookies(String cookieName) { @Test void extractsSessionIdFromMatomoSessIdCookie() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(singleton(new Cookie("MATOMO_SESSID", "2cbf8b5ba00fbf9ba70853308cd0944a"))); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .cookies(new CookieWrapper[] { + new CookieWrapper( + "MATOMO_SESSID", + "2cbf8b5ba00fbf9ba70853308cd0944a" + ) + }) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -120,11 +140,15 @@ void extractsSessionIdFromMatomoSessIdCookie() { @Test void parsesVisitCustomVariablesFromCookie() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(singleton(new Cookie( - "_pk_cvar.1.1fff", - "{\"1\":[\"VAR 1 set, var 2 not set\",\"yes\"],\"3\":[\"var 3 set\",\"yes!!!!\"]}" - ))); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .cookies(new CookieWrapper[] { + new CookieWrapper( + "_pk_cvar.1.1fff", + "{\"1\":[\"VAR 1 set, var 2 not set\",\"yes\"],\"3\":[\"var 3 set\",\"yes!!!!\"]}" + ) + }) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); @@ -139,8 +163,23 @@ void parsesVisitCustomVariablesFromCookie() { @Test void determinerVisitorIpFromXForwardedForHeader() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setHeaders(singletonMap("X-Forwarded-For", "44.55.66.77")); + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap("x-forwarded-for", "44.55.66.77")) + .build(); + + MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); + + MatomoRequest matomoRequest = builder.build(); + assertThat(matomoRequest.getVisitorIp()).isEqualTo("44.55.66.77"); + } + + @Test + void determinerVisitorIpFromXRealIpHeader() { + HttpServletRequestWrapper request = HttpServletRequestWrapper + .builder() + .headers(singletonMap("x-real-ip", "44.55.66.77")) + .build(); MatomoRequest.MatomoRequestBuilder builder = ServletMatomoRequest.fromServletRequest(request); diff --git a/pom.xml b/pom.xml index 2c4f5cf8..4f8ee12c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.piwik.java.tracking @@ -62,24 +63,52 @@ 1.8 1.8 ${project.build.outputDirectory}/delombok - 3.1.5 + 1.18.30 + 2.0.9 - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - com.github.spotbugs spotbugs-annotations 4.8.0 provided + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + org.junit.jupiter + junit-jupiter + 5.10.1 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + 12.0.3 + @@ -205,7 +234,7 @@ org.projectlombok lombok - 1.18.30 + ${lombok.version} @@ -361,7 +390,8 @@ core java8 java11 - servlet + servlet-jakarta + servlet-javax spring test diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml new file mode 100644 index 00000000..e290d21b --- /dev/null +++ b/servlet-jakarta/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + + + org.piwik.java.tracking + matomo-java-tracker-parent + 3.0.2-SNAPSHOT + ../pom.xml + + + matomo-java-tracker-servlet-jakarta + 3.0.2-SNAPSHOT + jar + + Matomo Java Tracker Servlet Jakarta + Integrates Matomo Java Tracker into your Jakarta servlet based application + + + 11 + 11 + + + + + org.piwik.java.tracking + matomo-java-tracker-core + ${project.version} + + + jakarta.servlet + jakarta.servlet-api + 6.0.0 + provided + + + org.slf4j + slf4j-api + + + com.github.spotbugs + spotbugs-annotations + + + org.projectlombok + lombok + provided + + + org.junit.jupiter + junit-jupiter + test + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + 12.0.3 + test + + + org.assertj + assertj-core + test + + + org.slf4j + slf4j-simple + test + + + + diff --git a/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java new file mode 100644 index 00000000..51b85554 --- /dev/null +++ b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapper.java @@ -0,0 +1,49 @@ +package org.matomo.java.tracking.servlet; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.NonNull; + +/** + * Converts a Jakarta {@link HttpServletRequest} to a {@link HttpServletRequestWrapper}. + */ +public final class JakartaHttpServletWrapper { + + private JakartaHttpServletWrapper() { + // utility + } + + /** + * Takes a Jakarta {@link HttpServletRequest} and converts it to a + * {@link HttpServletRequestWrapper}. + * + * @param request the request to convert to a wrapper object (must not be {@code null}). + * @return the wrapper object (never {@code null}). + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static HttpServletRequestWrapper fromHttpServletRequest(@NonNull HttpServletRequest request) { + Map headers = new LinkedHashMap<>(); + request.getHeaderNames() + .asIterator() + .forEachRemaining(name -> headers.put(name.toLowerCase(Locale.ROOT), request.getHeader(name))); + List cookies = null; + if (request.getCookies() != null) { + cookies = Stream.of(request.getCookies()) + .map(cookie -> new CookieWrapper(cookie.getName(), cookie.getValue())) + .collect(Collectors.toList()); + } + return HttpServletRequestWrapper + .builder() + .requestURL(request.getRequestURL()) + .remoteAddr(request.getRemoteAddr()) + .headers(headers) + .cookies(cookies == null ? null : cookies.toArray(new CookieWrapper[0])) + .build(); + } + +} diff --git a/servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java similarity index 75% rename from servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java rename to servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java index 82ebd557..664ce209 100644 --- a/servlet/src/main/java/org/matomo/java/tracking/MatomoTrackerFilter.java +++ b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java @@ -1,4 +1,4 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.servlet; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -8,6 +8,8 @@ import java.io.IOException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; /** * This filter can be used to automatically send a request to the Matomo server for every request @@ -22,7 +24,8 @@ public class MatomoTrackerFilter extends HttpFilter { @Override protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { - MatomoRequest matomoRequest = ServletMatomoRequest.fromServletRequest(req).build(); + MatomoRequest matomoRequest = ServletMatomoRequest + .fromServletRequest(JakartaHttpServletWrapper.fromHttpServletRequest(req)).build(); log.debug("Sending request {}", matomoRequest); tracker.sendRequestAsync(matomoRequest); super.doFilter(req, res, chain); diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java new file mode 100644 index 00000000..fac0d472 --- /dev/null +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java @@ -0,0 +1,63 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import org.eclipse.jetty.ee10.servlet.FilterHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.servlet.MatomoTrackerFilter; + +class MatomoTrackerFilterIT { + + @Test + void sendsAnAsyncRequestOnFilter() throws Exception { + + + TestSenderFactory senderFactory = new TestSenderFactory(); + + MatomoTracker tracker = new MatomoTracker( + TrackerConfiguration + .builder() + .apiEndpoint(URI.create("http://localhost:8080/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build()); + tracker.setSenderFactory(senderFactory); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addFilter(new FilterHolder(new MatomoTrackerFilter(tracker)), "/*", null); + Server server = new Server(0); + server.setHandler(context); + + server.start(); + URI uri = server.getURI(); + HttpClient.newHttpClient().send( + HttpRequest.newBuilder() + .header("Accept-Language", "en-US,en;q=0.9,de;q=0.8") + .uri(uri) + .build(), + HttpResponse.BodyHandlers.discarding() + ); + server.stop(); + + TestSender testSender = senderFactory.getTestSender(); + assertThat(testSender.getRequests()).hasSize(1).satisfiesExactly(matomoRequest -> { + assertThat(matomoRequest.getActionUrl()).isEqualTo(uri.toString()); + assertThat(matomoRequest.getVisitorId()).isNotNull(); + assertThat(matomoRequest.getVisitorIp()).isNotNull(); + assertThat(matomoRequest.getHeaders()).containsEntry( + "accept-language", + "en-US,en;q=0.9,de;q=0.8" + ); + }); + + } + +} \ No newline at end of file diff --git a/servlet/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java similarity index 100% rename from servlet/src/test/java/org/matomo/java/tracking/TestSender.java rename to servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java diff --git a/servlet/src/test/java/org/matomo/java/tracking/TestSenderFactory.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSenderFactory.java similarity index 100% rename from servlet/src/test/java/org/matomo/java/tracking/TestSenderFactory.java rename to servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSenderFactory.java diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java new file mode 100644 index 00000000..3d2699a7 --- /dev/null +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/JakartaHttpServletWrapperTest.java @@ -0,0 +1,35 @@ +package org.matomo.java.tracking.servlet; + +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.servlet.http.Cookie; +import java.util.List; +import org.junit.jupiter.api.Test; + +class JakartaHttpServletWrapperTest { + + @Test + void wrapsHttpServletRequest() { + + MockHttpServletRequest servlet = new MockHttpServletRequest(); + servlet.setRequestURL(new StringBuffer("http://localhost")); + servlet.setHeaders(singletonMap("Accept-Language", "en-US,en;q=0.9,de;q=0.8")); + servlet.setCookies(List.of(new Cookie("foo", "bar"))); + + HttpServletRequestWrapper httpServletRequestWrapper = + JakartaHttpServletWrapper.fromHttpServletRequest(servlet); + + assertThat(httpServletRequestWrapper.getRequestURL()).hasToString("http://localhost"); + assertThat(httpServletRequestWrapper.getHeaders()) + .containsEntry("accept-language", "en-US,en;q=0.9,de;q=0.8"); + assertThat(httpServletRequestWrapper.getCookies()) + .hasSize(1) + .satisfiesExactly(cookieWrapper -> { + assertThat(cookieWrapper.getName()).isEqualTo("foo"); + assertThat(cookieWrapper.getValue()).isEqualTo("bar"); + }); + } + +} + diff --git a/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java similarity index 98% rename from servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java rename to servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java index f0f6341a..d13398a6 100644 --- a/servlet/src/test/java/org/matomo/java/tracking/MockHttpServletRequest.java +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java @@ -1,4 +1,4 @@ -package org.matomo.java.tracking; +package org.matomo.java.tracking.servlet; import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; @@ -25,11 +25,15 @@ import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; +import lombok.Getter; import lombok.Setter; @Setter +@Getter class MockHttpServletRequest implements HttpServletRequest { + private StringBuffer requestURL; + private Map headers = new LinkedHashMap<>(); private Collection cookies; @@ -119,11 +123,6 @@ public String getRequestURI() { return null; } - @Override - public StringBuffer getRequestURL() { - return null; - } - @Override public String getServletPath() { return null; diff --git a/servlet/pom.xml b/servlet-javax/pom.xml similarity index 84% rename from servlet/pom.xml rename to servlet-javax/pom.xml index d9cfe71d..f907b9c5 100644 --- a/servlet/pom.xml +++ b/servlet-javax/pom.xml @@ -8,12 +8,12 @@ ../pom.xml - matomo-java-tracker-servlet + matomo-java-tracker-servlet-javax 3.0.2-SNAPSHOT jar - Matomo Java Tracker Servlet - Integrates Matomo Java Tracker into your servlet based application + Matomo Java Tracker Servlet Javax + Integrates Matomo Java Tracker into your javax servlet based application 11 @@ -27,8 +27,9 @@ ${project.version} - jakarta.servlet - jakarta.servlet-api + javax.servlet + javax.servlet-api + 4.0.1 provided @@ -52,6 +53,7 @@ org.eclipse.jetty jetty-servlet + 9.4.53.v20231009 test diff --git a/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java new file mode 100644 index 00000000..cee1a5ac --- /dev/null +++ b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapper.java @@ -0,0 +1,49 @@ +package org.matomo.java.tracking.servlet; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.servlet.http.HttpServletRequest; +import lombok.NonNull; + +/** + * Converts a javax {@link HttpServletRequest} to a {@link HttpServletRequestWrapper}. + */ +public final class JavaxHttpServletWrapper { + + private JavaxHttpServletWrapper() { + // utility + } + + /** + * Takes a javax {@link HttpServletRequest} and converts it to a + * {@link HttpServletRequestWrapper}. + * + * @param request the request to convert to a wrapper object (must not be {@code null}). + * @return the wrapper object (never {@code null}). + */ + @edu.umd.cs.findbugs.annotations.NonNull + public static HttpServletRequestWrapper fromHttpServletRequest(@NonNull HttpServletRequest request) { + Map headers = new LinkedHashMap<>(); + request.getHeaderNames() + .asIterator() + .forEachRemaining(name -> headers.put(name.toLowerCase(Locale.ROOT), request.getHeader(name))); + List cookies = null; + if (request.getCookies() != null) { + cookies = Stream.of(request.getCookies()) + .map(cookie -> new CookieWrapper(cookie.getName(), cookie.getValue())) + .collect(Collectors.toList()); + } + return HttpServletRequestWrapper + .builder() + .requestURL(request.getRequestURL()) + .remoteAddr(request.getRemoteAddr()) + .headers(headers) + .cookies(cookies == null ? null : cookies.toArray(new CookieWrapper[0])) + .build(); + } + +} diff --git a/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java new file mode 100644 index 00000000..abf0849d --- /dev/null +++ b/servlet-javax/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java @@ -0,0 +1,35 @@ +package org.matomo.java.tracking.servlet; + +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; + +/** + * This filter can be used to automatically send a request to the Matomo server for every request + * that is received by the servlet container. + */ +@RequiredArgsConstructor +@Slf4j +public class MatomoTrackerFilter extends HttpFilter { + + private final MatomoTracker tracker; + + @Override + protected void doFilter(@NonNull HttpServletRequest req, @NonNull HttpServletResponse res, + @NonNull FilterChain chain) + throws IOException, ServletException { + MatomoRequest matomoRequest = ServletMatomoRequest + .fromServletRequest(JavaxHttpServletWrapper.fromHttpServletRequest(req)).build(); + log.debug("Sending request {}", matomoRequest); + tracker.sendRequestAsync(matomoRequest); + super.doFilter(req, res, chain); + } +} diff --git a/servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java b/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java similarity index 95% rename from servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java rename to servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java index c33ae2ba..62979d9b 100644 --- a/servlet/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java @@ -10,6 +10,7 @@ import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.Test; +import org.matomo.java.tracking.servlet.MatomoTrackerFilter; class MatomoTrackerFilterIT { @@ -52,7 +53,7 @@ void sendsAnAsyncRequestOnFilter() throws Exception { assertThat(matomoRequest.getVisitorId()).isNotNull(); assertThat(matomoRequest.getVisitorIp()).isNotNull(); assertThat(matomoRequest.getHeaders()).containsEntry( - "Accept-Language", + "accept-language", "en-US,en;q=0.9,de;q=0.8" ); }); diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java new file mode 100644 index 00000000..d6c03833 --- /dev/null +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java @@ -0,0 +1,54 @@ +package org.matomo.java.tracking; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * A {@link Sender} implementation that does not send anything but stores the requests. + * + *

This class is intended for testing purposes only. It does not send anything to the Matomo server. Instead, it + * stores the requests and queries in collections that can be accessed via {@link #getRequests()}. + */ +@RequiredArgsConstructor +@Getter +class TestSender implements Sender { + + private final Collection requests = new ArrayList<>(); + + private final TrackerConfiguration trackerConfiguration; + + private final QueryCreator queryCreator; + + @NonNull + @Override + public CompletableFuture sendSingleAsync(@NonNull MatomoRequest request) { + requests.add(request); + return CompletableFuture.completedFuture(request); + } + + @Override + public void sendSingle(@NonNull MatomoRequest request) { + throw new UnsupportedOperationException(); + } + + @Override + public void sendBulk( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + throw new UnsupportedOperationException(); + } + + @NonNull + @Override + public CompletableFuture sendBulkAsync( + @NonNull Iterable requests, @Nullable String overrideAuthToken + ) { + throw new UnsupportedOperationException(); + } + +} diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSenderFactory.java b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSenderFactory.java new file mode 100644 index 00000000..fafafa90 --- /dev/null +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSenderFactory.java @@ -0,0 +1,16 @@ +package org.matomo.java.tracking; + +import lombok.Getter; + +class TestSenderFactory implements SenderFactory { + + @Getter + private TestSender testSender; + + @Override + public Sender createSender(TrackerConfiguration trackerConfiguration, QueryCreator queryCreator) { + TestSender testSender = new TestSender(trackerConfiguration, queryCreator); + this.testSender = testSender; + return testSender; + } +} diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java new file mode 100644 index 00000000..411a3919 --- /dev/null +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/JavaxHttpServletWrapperTest.java @@ -0,0 +1,35 @@ +package org.matomo.java.tracking.servlet; + +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import javax.servlet.http.Cookie; +import org.junit.jupiter.api.Test; + +class JavaxHttpServletWrapperTest { + + @Test + void wrapsHttpServletRequest() { + + MockHttpServletRequest servlet = new MockHttpServletRequest(); + servlet.setRequestURL(new StringBuffer("http://localhost")); + servlet.setHeaders(singletonMap("Accept-Language", "en-US,en;q=0.9,de;q=0.8")); + servlet.setCookies(List.of(new Cookie("foo", "bar"))); + + HttpServletRequestWrapper httpServletRequestWrapper = + JavaxHttpServletWrapper.fromHttpServletRequest(servlet); + + assertThat(httpServletRequestWrapper.getRequestURL()).hasToString("http://localhost"); + assertThat(httpServletRequestWrapper.getHeaders()) + .containsEntry("accept-language", "en-US,en;q=0.9,de;q=0.8"); + assertThat(httpServletRequestWrapper.getCookies()) + .hasSize(1) + .satisfiesExactly(cookieWrapper -> { + assertThat(cookieWrapper.getName()).isEqualTo("foo"); + assertThat(cookieWrapper.getValue()).isEqualTo("bar"); + }); + } + +} + diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java new file mode 100644 index 00000000..cb0924b3 --- /dev/null +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/servlet/MockHttpServletRequest.java @@ -0,0 +1,381 @@ +package org.matomo.java.tracking.servlet; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.Part; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +class MockHttpServletRequest implements HttpServletRequest { + + private StringBuffer requestURL; + + private Map headers = new LinkedHashMap<>(); + + private Collection cookies; + + @Override + public String getAuthType() { + return null; + } + + @Override + public Cookie[] getCookies() { + return cookies == null ? null : cookies.toArray(new Cookie[0]); + } + + @Override + public long getDateHeader(String name) { + return 0; + } + + @Override + public String getHeader(String name) { + return headers.get(name); + } + + @Override + public Enumeration getHeaders(String name) { + return null; + } + + @Override + public Enumeration getHeaderNames() { + return headers == null ? Collections.emptyEnumeration() : Collections.enumeration(headers.keySet()); + } + + @Override + public int getIntHeader(String name) { + return 0; + } + + @Override + public String getMethod() { + return null; + } + + @Override + public String getPathInfo() { + return null; + } + + @Override + public String getPathTranslated() { + return null; + } + + @Override + public String getContextPath() { + return null; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public boolean isUserInRole(String role) { + return false; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public String getRequestURI() { + return null; + } + + @Override + public String getServletPath() { + return null; + } + + @Override + public HttpSession getSession(boolean create) { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public String changeSessionId() { + return null; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + @Override + public void login(String username, String password) throws ServletException { + + } + + @Override + public void logout() throws ServletException { + + } + + @Override + public Collection getParts() throws IOException, ServletException { + return null; + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + return null; + } + + @Override + public T upgrade(Class handlerClass) throws IOException, ServletException { + return null; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public void setCharacterEncoding(String env) throws UnsupportedEncodingException { + + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public long getContentLengthLong() { + return 0; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getParameter(String name) { + return null; + } + + @Override + public Enumeration getParameterNames() { + return null; + } + + @Override + public String[] getParameterValues(String name) { + return new String[0]; + } + + @Override + public Map getParameterMap() { + return null; + } + + @Override + public String getProtocol() { + return null; + } + + @Override + public String getScheme() { + return null; + } + + @Override + public String getServerName() { + return null; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public BufferedReader getReader() throws IOException { + return null; + } + + @Override + public String getRemoteAddr() { + return null; + } + + @Override + public String getRemoteHost() { + return null; + } + + @Override + public void setAttribute(String name, Object o) { + + } + + @Override + public void removeAttribute(String name) { + + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public Enumeration getLocales() { + return null; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public String getRealPath(String s) { + return null; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public String getLocalAddr() { + return null; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return null; + } + +} diff --git a/spring/pom.xml b/spring/pom.xml index 555af204..5647910a 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -17,8 +17,21 @@ 17 17 + 3.1.5 + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + org.piwik.java.tracking @@ -27,7 +40,7 @@ org.piwik.java.tracking - matomo-java-tracker-servlet + matomo-java-tracker-servlet-jakarta ${project.version} diff --git a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java index 3529e537..e79bc8f8 100644 --- a/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java +++ b/spring/src/main/java/org/matomo/java/tracking/spring/MatomoTrackerAutoConfiguration.java @@ -10,8 +10,8 @@ import java.net.URI; import java.util.List; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.MatomoTrackerFilter; import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.servlet.MatomoTrackerFilter; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; diff --git a/test/pom.xml b/test/pom.xml index 22bc9205..e841a196 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -29,7 +29,7 @@ org.piwik.java.tracking - matomo-java-tracker-servlet + matomo-java-tracker-servlet-jakarta ${project.version} @@ -47,8 +47,9 @@ slf4j-simple - org.eclipse.jetty - jetty-servlet + org.eclipse.jetty.ee10 + jetty-ee10-servlet + 12.0.3 diff --git a/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java b/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java index 5681c5c4..0b4d9174 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java +++ b/test/src/main/java/org/matomo/java/tracking/test/MatomoServletTester.java @@ -2,14 +2,14 @@ import java.net.URI; import lombok.extern.slf4j.Slf4j; +import org.eclipse.jetty.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.servlet.FilterHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.DefaultServlet; -import org.eclipse.jetty.servlet.FilterHolder; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.MatomoTrackerFilter; import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.servlet.MatomoTrackerFilter; @Slf4j class MatomoServletTester { diff --git a/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java b/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java index 8e1c9547..29990973 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/ServletMatomoRequestExample.java @@ -3,7 +3,8 @@ import jakarta.servlet.http.HttpServletRequest; import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoTracker; -import org.matomo.java.tracking.ServletMatomoRequest; +import org.matomo.java.tracking.servlet.JakartaHttpServletWrapper; +import org.matomo.java.tracking.servlet.ServletMatomoRequest; /** * This is an example of how to use the ServletMatomoRequest class. @@ -23,7 +24,7 @@ public ServletMatomoRequestExample(MatomoTracker tracker) { */ public void someControllerMethod(HttpServletRequest req) { MatomoRequest matomoRequest = ServletMatomoRequest - .fromServletRequest(req) + .fromServletRequest(JakartaHttpServletWrapper.fromHttpServletRequest(req)) .actionName("Some Controller Action") // ... .build();