From 35cb2b6e455ea07d36a1f7c4e8e58ac2af031d2d Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 3 Jul 2024 16:07:29 +0200 Subject: [PATCH] Prevent NPE in micrometer when there is no response & 404 Signed-off-by: jansupol --- .../micrometer/server/JerseyKeyValues.java | 18 +++++- .../server/exception/JerseyKeyValuesTest.java | 62 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/exception/JerseyKeyValuesTest.java diff --git a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyKeyValues.java b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyKeyValues.java index 66cdaf7b1e..e6d5abc65d 100644 --- a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyKeyValues.java +++ b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyKeyValues.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -23,6 +23,9 @@ import org.glassfish.jersey.server.ExtendedUriInfo; import org.glassfish.jersey.server.monitoring.RequestEvent; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + /** * Factory methods for {@link KeyValue KeyValues} associated with a request-response * exchange that is handled by Jersey server. @@ -82,15 +85,26 @@ static KeyValue status(ContainerResponse response) { * @return the uri KeyValue derived from the request event */ static KeyValue uri(RequestEvent event) { + int status = 0; ContainerResponse response = event.getContainerResponse(); if (response != null) { - int status = response.getStatus(); + status = response.getStatus(); + } else if (response == null && WebApplicationException.class.isInstance(event.getException())) { + Response webAppResponse = ((WebApplicationException) event.getException()).getResponse(); + if (webAppResponse != null) { + status = webAppResponse.getStatus(); + } + } + if (status != 0) { if (JerseyTags.isRedirection(status) && event.getUriInfo().getMatchedResourceMethod() == null) { return URI_REDIRECTION; } if (status == 404 && event.getUriInfo().getMatchedResourceMethod() == null) { return URI_NOT_FOUND; } + if (status >= 500 && status <= 599) { + return STATUS_SERVER_ERROR; + } } String matchingPattern = JerseyTags.getMatchingPattern(event); if (matchingPattern.equals("/")) { diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/exception/JerseyKeyValuesTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/exception/JerseyKeyValuesTest.java new file mode 100644 index 0000000000..7f0358203c --- /dev/null +++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/exception/JerseyKeyValuesTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.micrometer.server.exception; + +import io.micrometer.common.KeyValue; +import io.micrometer.common.KeyValues; +import org.glassfish.jersey.micrometer.server.DefaultJerseyObservationConvention; +import org.glassfish.jersey.micrometer.server.JerseyContext; +import org.glassfish.jersey.server.ExtendedUriInfo; +import org.glassfish.jersey.server.internal.monitoring.RequestEventImpl; +import org.glassfish.jersey.server.monitoring.RequestEvent; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import javax.ws.rs.NotFoundException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.Optional; + +public class JerseyKeyValuesTest { + @Test + public void testOnException() { + ExtendedUriInfo uriInfo = (ExtendedUriInfo) Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[]{ExtendedUriInfo.class}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + switch (method.getName()) { + case "getMatchedTemplates": + return Collections.emptyList(); + } + return null; + } + }); + RequestEventImpl event = new RequestEventImpl.Builder() + .setExtendedUriInfo(uriInfo) + .setException(new NotFoundException(), RequestEvent.ExceptionCause.ORIGINAL) + .build(RequestEvent.Type.ON_EXCEPTION); + JerseyContext context = new JerseyContext(event); + DefaultJerseyObservationConvention convention = new DefaultJerseyObservationConvention("Test-Metric"); + KeyValues values = convention.getLowCardinalityKeyValues(context); + Optional kv = values.stream().filter(p -> p.getValue().equals("NOT_FOUND")).findFirst(); + MatcherAssert.assertThat(kv.isPresent(), Matchers.equalTo(true)); + } +}