Browse Source

Support JMX endpoints when only Jackson 2 is present

Closes gh-47688
pull/47784/head
Andy Wilkinson 2 months ago
parent
commit
4e94f26935
  1. 22
      module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointAutoConfiguration.java
  2. 80
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/Jackson2JmxOperationResponseMapper.java
  3. 2
      smoke-test/spring-boot-smoke-test-jackson2-only/src/main/resources/application.properties
  4. 16
      smoke-test/spring-boot-smoke-test-jackson2-only/src/test/java/smoketest/jackson2/only/SampleJackson2OnlyApplicationTests.java

22
module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointAutoConfiguration.java

@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.jmx; @@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.jmx;
import javax.management.MBeanServer;
import com.fasterxml.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.beans.factory.ObjectProvider;
@ -44,6 +45,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -44,6 +45,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
@ -133,4 +135,24 @@ public final class JmxEndpointAutoConfiguration { @@ -133,4 +135,24 @@ public final class JmxEndpointAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnMissingClass("tools.jackson.databind.json.JsonMapper")
@Deprecated(since = "4.2.0", forRemoval = true)
@SuppressWarnings("removal")
static class JmxJackson2EndpointConfiguration {
@Bean
@ConditionalOnSingleCandidate(MBeanServer.class)
JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer,
EndpointObjectNameFactory endpointObjectNameFactory, ObjectProvider<ObjectMapper> objectMapper,
JmxEndpointsSupplier jmxEndpointsSupplier) {
JmxOperationResponseMapper responseMapper = new org.springframework.boot.actuate.endpoint.jmx.Jackson2JmxOperationResponseMapper(
objectMapper.getIfAvailable());
return new JmxEndpointExporter(mBeanServer, endpointObjectNameFactory, responseMapper,
jmxEndpointsSupplier.getEndpoints());
}
}
}

80
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/Jackson2JmxOperationResponseMapper.java

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
/*
* Copyright 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.jmx;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
/**
* {@link JmxOperationResponseMapper} that delegates to a Jackson 2 {@link ObjectMapper}
* to return a JSON response.
*
* @author Stephane Nicoll
* @since 4.0.0
* @deprecated since 4.0.0 for removal in 4.2.0 in favor of
* {@link JacksonJmxOperationResponseMapper}.
*/
@Deprecated(since = "4.0.0", forRemoval = true)
public class Jackson2JmxOperationResponseMapper implements JmxOperationResponseMapper {
private final ObjectMapper objectMapper;
private final JavaType listType;
private final JavaType mapType;
public Jackson2JmxOperationResponseMapper(@Nullable ObjectMapper objectMapper) {
this.objectMapper = (objectMapper != null) ? objectMapper : new ObjectMapper();
this.listType = this.objectMapper.getTypeFactory().constructParametricType(List.class, Object.class);
this.mapType = this.objectMapper.getTypeFactory()
.constructParametricType(Map.class, String.class, Object.class);
}
@Override
public Class<?> mapResponseType(Class<?> responseType) {
if (CharSequence.class.isAssignableFrom(responseType)) {
return String.class;
}
if (responseType.isArray() || Collection.class.isAssignableFrom(responseType)) {
return List.class;
}
return Map.class;
}
@Override
@Contract("!null -> !null")
public @Nullable Object mapResponse(@Nullable Object response) {
if (response == null) {
return null;
}
if (response instanceof CharSequence) {
return response.toString();
}
if (response.getClass().isArray() || response instanceof Collection) {
return this.objectMapper.convertValue(response, this.listType);
}
return this.objectMapper.convertValue(response, this.mapType);
}
}

2
smoke-test/spring-boot-smoke-test-jackson2-only/src/main/resources/application.properties

@ -1 +1,3 @@ @@ -1 +1,3 @@
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*

16
smoke-test/spring-boot-smoke-test-jackson2-only/src/test/java/smoketest/jackson2/only/SampleJackson2OnlyApplicationTests.java

@ -16,8 +16,12 @@ @@ -16,8 +16,12 @@
package smoketest.jackson2.only;
import java.lang.management.ManagementFactory;
import java.util.Map;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -33,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -33,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Andy Wilkinson
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.jmx.enabled=true")
@AutoConfigureRestTestClient
class SampleJackson2OnlyApplicationTests {
@ -64,4 +68,14 @@ class SampleJackson2OnlyApplicationTests { @@ -64,4 +68,14 @@ class SampleJackson2OnlyApplicationTests {
.value((body) -> assertThat(body).containsOnlyKeys("_links"));
}
@Test
@SuppressWarnings("unchecked")
void jmxEndpointsShouldWork() throws Exception {
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
Map<String, Object> result = (Map<String, Object>) mbeanServer.invoke(
ObjectName.getInstance("org.springframework.boot:type=Endpoint,name=Configprops"),
"configurationProperties", new Object[0], null);
assertThat(result).containsOnlyKeys("contexts");
}
}

Loading…
Cancel
Save