Use configured ObjectMapper, if available, in all EndpointMBeans
Prior to this commit, every EndpointMBean used its own ObjectMapper. Each of these ObjectMappers was created using new ObjectMapper() with no opportunity for configuration. This commit uses the ObjectMapper from the application context and shares it among all EndpointMBeans. This gives the user control over the ObjectMapper’s configuration using spring.jackson.* properties, their own Jackson2ObjectMapperBuilder bean, etc. In the absence of an ObjectMapper in the application context a single ObjectMapper is instantiated and is used by all EndpointMBeans instead. To allow the ObjectMapper to be shared, a number of constructors have been overloaded to also take the ObjectMapper as a parameter. In these cases the old constructor has been preserved for backwards compatibility but has been deprecated. Closes gh-2393
This commit is contained in:
parent
8850286937
commit
becbc00a4d
|
|
@ -31,11 +31,14 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} to enable JMX export for
|
||||
* {@link Endpoint}s.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnExpression("${endpoints.jmx.enabled:true} && ${spring.jmx.enabled:true}")
|
||||
|
|
@ -44,11 +47,14 @@ import org.springframework.util.StringUtils;
|
|||
public class EndpointMBeanExportAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
EndpointMBeanExportProperties properties = new EndpointMBeanExportProperties();
|
||||
private EndpointMBeanExportProperties properties = new EndpointMBeanExportProperties();
|
||||
|
||||
@Autowired(required = false)
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Bean
|
||||
public EndpointMBeanExporter endpointMBeanExporter(MBeanServer server) {
|
||||
EndpointMBeanExporter mbeanExporter = new EndpointMBeanExporter();
|
||||
EndpointMBeanExporter mbeanExporter = new EndpointMBeanExporter(this.objectMapper);
|
||||
String domain = this.properties.getDomain();
|
||||
if (StringUtils.hasText(domain)) {
|
||||
mbeanExporter.setDomain(domain);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 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.
|
||||
|
|
@ -20,19 +20,28 @@ import org.springframework.boot.actuate.endpoint.Endpoint;
|
|||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Simple wrapper around {@link Endpoint} implementations that provide actuator data of
|
||||
* some sort.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ManagedResource
|
||||
public class DataEndpointMBean extends EndpointMBean {
|
||||
|
||||
@Deprecated
|
||||
public DataEndpointMBean(String beanName, Endpoint<?> endpoint) {
|
||||
super(beanName, endpoint);
|
||||
}
|
||||
|
||||
public DataEndpointMBean(String beanName, Endpoint<?> endpoint,
|
||||
ObjectMapper objectMapper) {
|
||||
super(beanName, endpoint, objectMapper);
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Invoke the underlying endpoint")
|
||||
public Object getData() {
|
||||
return convert(getEndpoint().invoke());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 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.
|
||||
|
|
@ -31,18 +31,26 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
* Simple wrapper around {@link Endpoint} implementations to enable JMX export.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ManagedResource
|
||||
public class EndpointMBean {
|
||||
|
||||
private final Endpoint<?> endpoint;
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
@Deprecated
|
||||
public EndpointMBean(String beanName, Endpoint<?> endpoint) {
|
||||
this(beanName, endpoint, new ObjectMapper());
|
||||
}
|
||||
|
||||
public EndpointMBean(String beanName, Endpoint<?> endpoint, ObjectMapper objectMapper) {
|
||||
Assert.notNull(beanName, "BeanName must not be null");
|
||||
Assert.notNull(endpoint, "Endpoint must not be null");
|
||||
Assert.notNull(objectMapper, "ObjectMapper must not be null");
|
||||
this.endpoint = endpoint;
|
||||
this.mapper = objectMapper;
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Returns the class of the underlying endpoint")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 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.
|
||||
|
|
@ -49,12 +49,15 @@ import org.springframework.jmx.export.naming.SelfNaming;
|
|||
import org.springframework.jmx.support.ObjectNameManager;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* {@link ApplicationListener} that registers all known {@link Endpoint}s with an
|
||||
* {@link MBeanServer} using the {@link MBeanExporter} located from the application
|
||||
* context.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecycle,
|
||||
BeanFactoryAware, ApplicationContextAware {
|
||||
|
|
@ -91,8 +94,14 @@ public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecyc
|
|||
|
||||
private Properties objectNameStaticProperties = new Properties();
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public EndpointMBeanExporter() {
|
||||
super();
|
||||
this(null);
|
||||
}
|
||||
|
||||
public EndpointMBeanExporter(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper == null ? new ObjectMapper() : objectMapper;
|
||||
setAutodetect(false);
|
||||
setNamingStrategy(this.defaultNamingStrategy);
|
||||
setAssembler(this.assembler);
|
||||
|
|
@ -168,9 +177,9 @@ public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecyc
|
|||
|
||||
protected EndpointMBean getEndpointMBean(String beanName, Endpoint<?> endpoint) {
|
||||
if (endpoint instanceof ShutdownEndpoint) {
|
||||
return new ShutdownEndpointMBean(beanName, endpoint);
|
||||
return new ShutdownEndpointMBean(beanName, endpoint, this.objectMapper);
|
||||
}
|
||||
return new DataEndpointMBean(beanName, endpoint);
|
||||
return new DataEndpointMBean(beanName, endpoint, this.objectMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 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.
|
||||
|
|
@ -21,18 +21,27 @@ import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
|
|||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Special endpoint wrapper for {@link ShutdownEndpoint}.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ManagedResource
|
||||
public class ShutdownEndpointMBean extends EndpointMBean {
|
||||
|
||||
@Deprecated
|
||||
public ShutdownEndpointMBean(String beanName, Endpoint<?> endpoint) {
|
||||
super(beanName, endpoint);
|
||||
}
|
||||
|
||||
public ShutdownEndpointMBean(String beanName, Endpoint<?> endpoint,
|
||||
ObjectMapper mapper) {
|
||||
super(beanName, endpoint, mapper);
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Shutdown the ApplicationContext")
|
||||
public Object shutdown() {
|
||||
return convert(getEndpoint().invoke());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 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.
|
||||
|
|
@ -16,8 +16,11 @@
|
|||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
|
|
@ -28,6 +31,7 @@ import javax.management.ObjectName;
|
|||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
|
@ -36,13 +40,19 @@ import org.springframework.jmx.export.MBeanExporter;
|
|||
import org.springframework.jmx.support.ObjectNameManager;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointMBeanExporter}
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class EndpointMBeanExporterTests {
|
||||
|
||||
|
|
@ -176,6 +186,47 @@ public class EndpointMBeanExporterTests {
|
|||
parent.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonConversionWithDefaultObjectMapper() throws Exception {
|
||||
this.context = new GenericApplicationContext();
|
||||
this.context.registerBeanDefinition("endpointMbeanExporter",
|
||||
new RootBeanDefinition(EndpointMBeanExporter.class));
|
||||
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
|
||||
JsonConversionEndpoint.class));
|
||||
this.context.refresh();
|
||||
|
||||
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
|
||||
Object response = mbeanExporter.getServer().invoke(
|
||||
getObjectName("endpoint1", this.context), "getData", new Object[0],
|
||||
new String[0]);
|
||||
|
||||
assertThat(response, is(instanceOf(Map.class)));
|
||||
assertThat(((Map<?, ?>) response).get("date"), is(instanceOf(Long.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonConversionWithCustomObjectMapper() throws Exception {
|
||||
this.context = new GenericApplicationContext();
|
||||
ConstructorArgumentValues constructorArgs = new ConstructorArgumentValues();
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
|
||||
constructorArgs.addIndexedArgumentValue(0, objectMapper);
|
||||
this.context
|
||||
.registerBeanDefinition("endpointMbeanExporter", new RootBeanDefinition(
|
||||
EndpointMBeanExporter.class, constructorArgs, null));
|
||||
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
|
||||
JsonConversionEndpoint.class));
|
||||
this.context.refresh();
|
||||
|
||||
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
|
||||
Object response = mbeanExporter.getServer().invoke(
|
||||
getObjectName("endpoint1", this.context), "getData", new Object[0],
|
||||
new String[0]);
|
||||
|
||||
assertThat(response, is(instanceOf(Map.class)));
|
||||
assertThat(((Map<?, ?>) response).get("date"), is(instanceOf(String.class)));
|
||||
}
|
||||
|
||||
private ObjectName getObjectName(String beanKey, GenericApplicationContext context)
|
||||
throws MalformedObjectNameException {
|
||||
return getObjectName("org.springframework.boot", beanKey, false, context);
|
||||
|
|
@ -209,4 +260,20 @@ public class EndpointMBeanExporterTests {
|
|||
}
|
||||
}
|
||||
|
||||
public static class JsonConversionEndpoint extends
|
||||
AbstractEndpoint<Map<String, Object>> {
|
||||
|
||||
public JsonConversionEndpoint() {
|
||||
super("json-conversion");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>();
|
||||
result.put("date", new Date());
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue