Shorten internal component names for graph (#3128)

* Shorten internal component names for graph

The long `IntegrationContextUtils.ERROR_LOGGER_BEAN_NAME` name
is used for `errorChannel` `LoggingHandler`'s endpoint.
It is too long for endpoint node in the graph meanwhile the
`errorChannel` is good everywhere

* Change an `IntegrationGraphServer` logic to shorten internal
component names before drawing a graph
* Introduce `IntegrationUtils.obtainComponentName()` for
shortening an internal name if any
* Use `IntegrationConfigUtils.BASE_PACKAGE` in the `IntegrationMBeanExporter`
instead of its own static property
* Remove `IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE` in favor
of introduced in SF `FactoryBean.OBJECT_TYPE_ATTRIBUTE`
* Rework affected test classes to JUnit 5
* Remove usage of deprecated options in the `@EnableIntegrationManagement`
* Document names shortening feature for graph

* * Improve `obtainComponentName()` function
This commit is contained in:
Artem Bilan
2019-12-19 17:02:24 -05:00
parent c08f4ec5c6
commit fab7741368
9 changed files with 61 additions and 74 deletions

View File

@@ -35,9 +35,6 @@ public final class IntegrationConfigUtils {
public static final String HANDLER_ALIAS_SUFFIX = ".handler";
// TODO: Boot constant - move to Spring Framework?
public static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
public static void registerSpelFunctionBean(BeanDefinitionRegistry registry, String functionId, String className,
String methodSignature) {

View File

@@ -23,8 +23,8 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.beans.BeanMetadataAttribute;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -176,9 +176,7 @@ public class MessagingGatewayRegistrar implements ImportBeanDefinitionRegistrar
gatewayProxyBuilder.addConstructorArgValue(serviceInterface);
AbstractBeanDefinition beanDefinition = gatewayProxyBuilder.getBeanDefinition();
beanDefinition.addMetadataAttribute(new BeanMetadataAttribute(IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE,
serviceInterface));
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, serviceInterface);
return new BeanDefinitionHolder(beanDefinition, id);
}
@@ -188,7 +186,7 @@ public class MessagingGatewayRegistrar implements ImportBeanDefinitionRegistrar
* @param importingClassMetadata The importing class metadata
* @return The captured values.
*/
private static List<MultiValueMap<String, Object>> captureMetaAnnotationValues(
private static List<MultiValueMap<String, Object>> captureMetaAnnotationValues(
AnnotationMetadata importingClassMetadata) {
Set<String> directAnnotations = importingClassMetadata.getAnnotationTypes();

View File

@@ -48,6 +48,7 @@ import org.springframework.integration.router.RecipientListRouter.Recipient;
import org.springframework.integration.router.RecipientListRouterManagement;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.integration.support.management.MappingMessageRouterManagement;
import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.lang.Nullable;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
@@ -65,7 +66,7 @@ import org.springframework.util.ClassUtils;
*/
public class IntegrationGraphServer implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
private static final float GRAPH_VERSION = 1.1f;
private static final float GRAPH_VERSION = 1.2f;
private static MicrometerNodeEnhancer micrometerEnhancer;
@@ -377,7 +378,7 @@ public class IntegrationGraphServer implements ApplicationContextAware, Applicat
}
@Nullable
String channelToBeanName(MessageChannel messageChannel) {
private String channelToBeanName(MessageChannel messageChannel) {
return messageChannel instanceof NamedComponent
? ((NamedComponent) messageChannel).getBeanName()
: Objects.toString(messageChannel, null);
@@ -396,7 +397,7 @@ public class IntegrationGraphServer implements ApplicationContextAware, Applicat
String nameToUse = name;
MessageSource<?> source = adapter.getMessageSource();
if (source instanceof NamedComponent) {
nameToUse = ((NamedComponent) source).getComponentName();
nameToUse = IntegrationUtils.obtainComponentName((NamedComponent) source);
}
MessageSourceNode node = new MessageSourceNode(this.nodeId.incrementAndGet(), nameToUse, source,
outputChannel, errorChannel);
@@ -412,7 +413,7 @@ public class IntegrationGraphServer implements ApplicationContextAware, Applicat
MessageHandlerNode node;
String name = nameArg;
if (handler instanceof NamedComponent) {
name = ((NamedComponent) handler).getComponentName();
name = IntegrationUtils.obtainComponentName((NamedComponent) handler);
}
if (handler instanceof CompositeMessageHandler) {
node = compositeHandler(name, consumer, (CompositeMessageHandler) handler, outputChannelName, null,
@@ -448,7 +449,7 @@ public class IntegrationGraphServer implements ApplicationContextAware, Applicat
MessageHandlerNode node;
String name = nameArg;
if (handler instanceof NamedComponent) {
name = ((NamedComponent) handler).getComponentName();
name = IntegrationUtils.obtainComponentName((NamedComponent) handler);
}
if (handler instanceof CompositeMessageHandler) {
node = compositeHandler(name, consumer, (CompositeMessageHandler) handler, outputChannelName,
@@ -487,7 +488,7 @@ public class IntegrationGraphServer implements ApplicationContextAware, Applicat
.map(NamedComponent.class::cast)
.map(named ->
new CompositeMessageHandlerNode.InnerHandler(
named.getComponentName(),
IntegrationUtils.obtainComponentName(named),
named.getComponentType()))
.collect(Collectors.toList());

View File

@@ -24,8 +24,11 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.integration.config.IntegrationConfigUtils;
import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.MessageBuilderFactory;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.MessageHandlingException;
@@ -44,18 +47,19 @@ import org.springframework.util.Assert;
*/
public final class IntegrationUtils {
private static final Log logger = LogFactory.getLog(IntegrationUtils.class);
private static final Log LOGGER = LogFactory.getLog(IntegrationUtils.class);
private static final String INTERNAL_COMPONENT_PREFIX = '_' + IntegrationConfigUtils.BASE_PACKAGE;
public static final String INTEGRATION_CONVERSION_SERVICE_BEAN_NAME = "integrationConversionService";
public static final String INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME = "messageBuilderFactory";
/**
* Should be set to TRUE on CI plans and framework developer systems.
*/
public static final boolean fatalWhenNoBeanFactory =
Boolean.valueOf(System.getenv("SI_FATAL_WHEN_NO_BEANFACTORY"));
Boolean.parseBoolean(System.getenv("SI_FATAL_WHEN_NO_BEANFACTORY"));
private IntegrationUtils() {
super();
@@ -75,7 +79,7 @@ public final class IntegrationUtils {
* @param beanFactory The bean factory.
* @return The message builder factory.
*/
public static MessageBuilderFactory getMessageBuilderFactory(BeanFactory beanFactory) {
public static MessageBuilderFactory getMessageBuilderFactory(@Nullable BeanFactory beanFactory) {
MessageBuilderFactory messageBuilderFactory = null;
if (beanFactory != null) {
try {
@@ -83,8 +87,8 @@ public final class IntegrationUtils {
INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME, MessageBuilderFactory.class);
}
catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("No MessageBuilderFactory with name '"
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("No MessageBuilderFactory with name '"
+ INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME
+ "' found: " + e.getMessage()
+ ", using default.");
@@ -92,9 +96,7 @@ public final class IntegrationUtils {
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No 'beanFactory' supplied; cannot find MessageBuilderFactory, using default.");
}
LOGGER.debug("No 'beanFactory' supplied; cannot find MessageBuilderFactory, using default.");
if (fatalWhenNoBeanFactory) {
throw new IllegalStateException("All Message creators need a BeanFactory");
}
@@ -193,4 +195,18 @@ public final class IntegrationUtils {
return runtimeException;
}
/**
* Obtain a component name from the provided {@link NamedComponent}.
* @param component the {@link NamedComponent} source for component name.
* @return the component name
* @since 5.3
*/
public static String obtainComponentName(NamedComponent component) {
String name = component.getComponentName();
if (name.charAt(0) == '_' && name.startsWith(INTERNAL_COMPONENT_PREFIX)) {
name = name.substring((INTERNAL_COMPONENT_PREFIX).length() + 1);
}
return name;
}
}

View File

@@ -35,6 +35,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
@@ -42,7 +43,6 @@ import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.expression.Expression;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.IntegrationConfigUtils;
import org.springframework.integration.gateway.GatewayMethodMetadata;
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
import org.springframework.integration.gateway.RequestReplyExchanger;
@@ -177,16 +177,16 @@ public class GatewayParserTests {
@Test
public void testFactoryBeanObjectTypeWithServiceInterface() {
ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext) context).getBeanFactory();
Object attribute = beanFactory.getMergedBeanDefinition("&oneWay").getAttribute(
IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE);
Object attribute =
beanFactory.getMergedBeanDefinition("&oneWay").getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE);
assertThat(attribute).isEqualTo(TestService.class.getName());
}
@Test
public void testFactoryBeanObjectTypeWithNoServiceInterface() {
ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext) context).getBeanFactory();
Object attribute = beanFactory.getMergedBeanDefinition("&defaultConfig").getAttribute(
IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE);
Object attribute =
beanFactory.getMergedBeanDefinition("&defaultConfig").getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE);
assertThat(attribute).isEqualTo(RequestReplyExchanger.class.getName());
}

View File

@@ -24,9 +24,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
@@ -43,8 +42,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@@ -62,8 +60,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
*
* @since 4.3
*/
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringJUnitWebConfig
@TestPropertySource(properties = "spring.application.name:testApplication")
@DirtiesContext
public class IntegrationGraphControllerTests {
@@ -73,7 +70,7 @@ public class IntegrationGraphControllerTests {
private MockMvc mockMvc;
@Before
@BeforeEach
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@@ -88,8 +85,7 @@ public class IntegrationGraphControllerTests {
.andExpect(handler().handlerType(IntegrationGraphController.class))
.andExpect(handler().methodName("getGraph"))
.andExpect(jsonPath("$.nodes..name")
.value(Matchers.containsInAnyOrder("nullChannel", "errorChannel",
"_org.springframework.integration.errorLogger")))
.value(Matchers.containsInAnyOrder("nullChannel", "errorChannel", "errorLogger")))
// .andDo(print())
.andExpect(jsonPath("$.contentDescriptor.name").value("testApplication"))
.andExpect(jsonPath("$.links").exists());
@@ -154,9 +150,7 @@ public class IntegrationGraphControllerTests {
@Configuration
@EnableWebMvc
@EnableIntegration
@EnableIntegrationManagement(statsEnabled = "_org.springframework.integration.errorLogger.handler",
countsEnabled = "!*",
defaultLoggingEnabled = "false")
@EnableIntegrationManagement(defaultLoggingEnabled = "false")
@EnableIntegrationGraphController(path = "/testIntegration", allowedOrigins = "https://foo.bar.com")
public static class ContextConfiguration {

View File

@@ -43,6 +43,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.Lifecycle;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.IntegrationConfigUtils;
import org.springframework.integration.config.IntegrationManagementConfigurer;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.context.OrderlyShutdownCapable;
@@ -111,9 +112,7 @@ import org.springframework.util.ReflectionUtils;
public class IntegrationMBeanExporter extends MBeanExporter
implements ApplicationContextAware, DestructionAwareBeanPostProcessor {
private static final String SI_PACKAGE = "org.springframework.integration";
public static final String DEFAULT_DOMAIN = SI_PACKAGE;
public static final String DEFAULT_DOMAIN = IntegrationConfigUtils.BASE_PACKAGE;
private final IntegrationJmxAttributeSource attributeSource = new IntegrationJmxAttributeSource();
@@ -827,7 +826,7 @@ public class IntegrationMBeanExporter extends MBeanExporter
String beanKey;
String name = endpoint.getComponentName();
String source;
if (name.startsWith('_' + SI_PACKAGE)) {
if (name.startsWith('_' + IntegrationConfigUtils.BASE_PACKAGE)) {
name = getInternalComponentName(name);
source = "internal";
}
@@ -881,7 +880,7 @@ public class IntegrationMBeanExporter extends MBeanExporter
private String getChannelBeanKey(String channel) {
String extra = "";
if (channel.startsWith(SI_PACKAGE)) {
if (channel.startsWith(IntegrationConfigUtils.BASE_PACKAGE)) {
extra = ",source=anonymous";
}
return String.format(this.domain + ":type=MessageChannel,name=%s%s" + getStaticNames(),
@@ -979,11 +978,11 @@ public class IntegrationMBeanExporter extends MBeanExporter
String managedType = source;
String managedName = name;
if (managedName != null && managedName.startsWith('_' + SI_PACKAGE)) {
if (managedName != null && managedName.startsWith('_' + IntegrationConfigUtils.BASE_PACKAGE)) {
managedName = getInternalComponentName(managedName);
managedType = "internal";
}
if (managedName != null && name.startsWith(SI_PACKAGE)) {
if (managedName != null && name.startsWith(IntegrationConfigUtils.BASE_PACKAGE)) {
MessageChannel inputChannel = endpoint.getInputChannel();
if (inputChannel != null) {
managedName = buildAnonymousManagedName(this.anonymousHandlerCounters, inputChannel);
@@ -1058,7 +1057,7 @@ public class IntegrationMBeanExporter extends MBeanExporter
}
private String getInternalComponentName(String name) {
return name.substring(('_' + SI_PACKAGE).length() + 1);
return name.substring(('_' + IntegrationConfigUtils.BASE_PACKAGE).length() + 1);
}
private org.springframework.integration.support.management.MessageSourceMetrics enhanceSourceMonitor(
@@ -1075,7 +1074,7 @@ public class IntegrationMBeanExporter extends MBeanExporter
if (endpoint != null) {
endpointName = endpoint.getBeanName();
}
if (endpointName != null && endpointName.startsWith('_' + SI_PACKAGE)) {
if (endpointName != null && endpointName.startsWith('_' + IntegrationConfigUtils.BASE_PACKAGE)) {
endpointName = getInternalComponentName(endpointName);
source = "internal";
}
@@ -1115,7 +1114,7 @@ public class IntegrationMBeanExporter extends MBeanExporter
String managedType = source;
String managedName = name;
if (managedName != null && managedName.startsWith(SI_PACKAGE)) {
if (managedName != null && managedName.startsWith(IntegrationConfigUtils.BASE_PACKAGE)) {
Object target = endpoint;
if (endpoint instanceof Advised) {
TargetSource targetSource = ((Advised) endpoint).getTargetSource();

View File

@@ -80,7 +80,7 @@ A Spring Integration application with only the default components would expose a
"max" : 0.0
}
},
"name" : "_org.springframework.integration.errorLogger"
"name" : "errorLogger"
} ],
"links" : [ {
"from" : 2,
@@ -143,29 +143,7 @@ The `nodes` graph element is perhaps the most interesting, because its elements
Node elements contain various properties that are generally self-explanatory.
For example, expression-based components include the `expression` property that contains the primary expression string for the component.
To enable the metrics, add an `@EnableIntegrationManagement` to a `@Configuration` class or add an `<int:management/>` element to your XML configuration.
You can control exactly which components in the framework collect statistics.
See <<./metrics.adoc#metrics-management,Metrics and Management>> for complete information.
See the `stats` attribute from the `o.s.i.errorLogger` component in the JSON example shown earlier.
In this case, The `nullChannel` and `errorChannel` do not provide statistics information, because the configuration for this example was as follows:
====
[source,java]
----
@Configuration
@EnableIntegration
@EnableIntegrationManagement(statsEnabled = "_org.springframework.integration.errorLogger.handler",
countsEnabled = "!*",
defaultLoggingEnabled = "false")
public class ManagementConfiguration {
@Bean
public IntegrationGraphServer integrationGraphServer() {
return new IntegrationGraphServer();
}
}
----
====
The `nodeId` represents a unique incremental identifier to let you distinguish one component from another.
It is also used in the `links` element to represent a relationship (connection) of this component to others, if any.

View File

@@ -26,3 +26,7 @@ See its JavaDocs and <<./graph.adoc#integration-graph,Integration Graph>> for mo
The gateway proxy now doesn't proxy `default` methods by default.
see <<./gateway.adoc/gateway-calling-default-methods,Invoking `default` Methods>> for more information.
Internal components (such as `_org.springframework.integration.errorLogger`) now have a shortened name when they are represented in the integration graph.
See <<./graph.adoc#integration-graph,Integration Graph>> for more information.