diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagedResource.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagedResource.java
index a2b641ca4c..c53b62d758 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagedResource.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagedResource.java
@@ -22,11 +22,13 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.springframework.core.annotation.AliasFor;
import org.springframework.jmx.export.annotation.ManagedResource;
/**
* Clone of {@link ManagedResource} limiting beans thus annotated so that they
- * will only be exported by the {@code IntegrationMBeanExporter}.
+ * will only be exported by the {@code IntegrationMBeanExporter} and prevented
+ * from being exported by other MBeanExporters (if present).
*
* @author Gary Russell
* @since 4.2
@@ -43,8 +45,10 @@ public @interface IntegrationManagedResource {
* attribute, for simple default usage.
* @return the value.
*/
+ @AliasFor("objectName")
String value() default "";
+ @AliasFor("value")
String objectName() default "";
String description() default "";
diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java
index 0205a9a983..d71534df41 100644
--- a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java
+++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java
@@ -25,8 +25,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import javax.management.Descriptor;
import javax.management.DynamicMBean;
import javax.management.JMException;
+import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.modelmbean.ModelMBean;
@@ -36,6 +38,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.BeansException;
+import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.annotation.AnnotationBeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -71,9 +74,10 @@ import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedMetric;
import org.springframework.jmx.export.annotation.ManagedOperation;
-import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler;
import org.springframework.jmx.export.metadata.InvalidMetadataException;
+import org.springframework.jmx.export.metadata.JmxAttributeSource;
+import org.springframework.jmx.export.metadata.ManagedResource;
import org.springframework.jmx.export.naming.MetadataNamingStrategy;
import org.springframework.jmx.support.MetricType;
import org.springframework.messaging.MessageChannel;
@@ -83,7 +87,6 @@ import org.springframework.util.PatternMatchUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;
-import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
/**
@@ -113,7 +116,7 @@ import org.springframework.util.StringValueResolver;
* @author Gary Russell
* @author Artem Bilan
*/
-@ManagedResource
+@org.springframework.jmx.export.annotation.ManagedResource
public class IntegrationMBeanExporter extends MBeanExporter implements ApplicationContextAware,
EmbeddedValueResolverAware {
@@ -121,7 +124,7 @@ public class IntegrationMBeanExporter extends MBeanExporter implements Applicati
public static final String DEFAULT_DOMAIN = "org.springframework.integration";
- private final AnnotationJmxAttributeSource attributeSource = new IntegrationJmxAttributeSource();
+ private final IntegrationJmxAttributeSource attributeSource = new IntegrationJmxAttributeSource();
private ApplicationContext applicationContext;
@@ -155,9 +158,9 @@ public class IntegrationMBeanExporter extends MBeanExporter implements Applicati
private final Properties objectNameStaticProperties = new Properties();
- private final MetadataMBeanInfoAssembler assembler = new MetadataMBeanInfoAssembler(attributeSource);
+ private final MetadataMBeanInfoAssembler assembler = new IntegrationMetadataMBeanInfoAssembler(attributeSource);
- private final MetadataNamingStrategy defaultNamingStrategy = new MetadataNamingStrategy(attributeSource);
+ private final MetadataNamingStrategy defaultNamingStrategy = new IntegrationMetadataNamingStrategy(attributeSource);
private String[] componentNamePatterns = { "*" };
@@ -165,8 +168,6 @@ public class IntegrationMBeanExporter extends MBeanExporter implements Applicati
private final AtomicBoolean shuttingDown = new AtomicBoolean();
- private StringValueResolver embeddedValueResolver;
-
public IntegrationMBeanExporter() {
super();
@@ -221,7 +222,7 @@ public class IntegrationMBeanExporter extends MBeanExporter implements Applicati
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
- this.embeddedValueResolver = resolver;
+ this.attributeSource.setValueResolver(resolver);
}
@Override
@@ -1067,29 +1068,63 @@ public class IntegrationMBeanExporter extends MBeanExporter implements Applicati
return ReflectionUtils.getField(field, target);
}
- private class IntegrationJmxAttributeSource extends AnnotationJmxAttributeSource {
+ private static Object extractManagedBean(Object managedBean) {
+ if (managedBean instanceof LifecycleMessageHandlerMetrics
+ || managedBean instanceof LifecycleMessageSourceMetrics) {
+ DirectFieldAccessor accessor = new DirectFieldAccessor(managedBean);
+ return accessor.getPropertyValue("delegate");
+ }
+ return managedBean;
+ }
+
+ private static class IntegrationJmxAttributeSource extends AnnotationJmxAttributeSource {
+
+ private StringValueResolver valueResolver;
+
+ void setValueResolver(StringValueResolver valueResolver) {
+ this.valueResolver = valueResolver;
+ }
@Override
- public org.springframework.jmx.export.metadata.ManagedResource getManagedResource(Class> beanClass)
- throws InvalidMetadataException {
- IntegrationManagedResource ann =
- AnnotationUtils.getAnnotation(beanClass, IntegrationManagedResource.class);
+ public ManagedResource getManagedResource(Class> beanClass) throws InvalidMetadataException {
+ IntegrationManagedResource ann = AnnotationUtils.getAnnotation(beanClass, IntegrationManagedResource.class);
if (ann == null) {
return null;
}
- org.springframework.jmx.export.metadata.ManagedResource managedResource =
- new org.springframework.jmx.export.metadata.ManagedResource();
- AnnotationBeanUtils.copyPropertiesToBean(ann, managedResource,
- IntegrationMBeanExporter.this.embeddedValueResolver);
- if (!"".equals(ann.value()) && !StringUtils.hasLength(managedResource.getObjectName())) {
- String value = ann.value();
- if (IntegrationMBeanExporter.this.embeddedValueResolver != null) {
- value = IntegrationMBeanExporter.this.embeddedValueResolver.resolveStringValue(value);
- }
- managedResource.setObjectName(value);
- }
+ ManagedResource managedResource = new ManagedResource();
+ AnnotationBeanUtils.copyPropertiesToBean(ann, managedResource, this.valueResolver);
return managedResource;
}
+ }
+
+ private static class IntegrationMetadataMBeanInfoAssembler extends MetadataMBeanInfoAssembler {
+
+ public IntegrationMetadataMBeanInfoAssembler(JmxAttributeSource attributeSource) {
+ super(attributeSource);
+ }
+
+ @Override
+ protected String getDescription(Object managedBean, String beanKey) {
+ return super.getDescription(extractManagedBean(managedBean), beanKey);
+ }
+
+ @Override
+ protected void populateMBeanDescriptor(Descriptor desc, Object managedBean, String beanKey) {
+ super.populateMBeanDescriptor(desc, extractManagedBean(managedBean), beanKey);
+ }
+
+ }
+
+ private static class IntegrationMetadataNamingStrategy extends MetadataNamingStrategy {
+
+ public IntegrationMetadataNamingStrategy(JmxAttributeSource attributeSource) {
+ super(attributeSource);
+ }
+
+ @Override
+ public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
+ return super.getObjectName(extractManagedBean(managedBean), beanKey);
+ }
}
diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/CustomObjectNameTests-context.xml b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/CustomObjectNameTests-context.xml
new file mode 100644
index 0000000000..ee04714f3b
--- /dev/null
+++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/CustomObjectNameTests-context.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ custom:type=MessageChannel,name=foo
+ custom:type=MessageHandler,name=foo
+ custom:type=MessageSource,name=foo
+ custom:type=MessageRouter,name=foo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/CustomObjectNameTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/CustomObjectNameTests.java
new file mode 100644
index 0000000000..af1467d571
--- /dev/null
+++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/CustomObjectNameTests.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 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. You may obtain a copy of the License at
+ *
+ * http://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.integration.jmx.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.management.Descriptor;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.channel.AbstractMessageChannel;
+import org.springframework.integration.endpoint.AbstractMessageSource;
+import org.springframework.integration.handler.AbstractMessageHandler;
+import org.springframework.integration.router.AbstractMappingMessageRouter;
+import org.springframework.integration.support.management.IntegrationManagedResource;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author Gary Russell
+ * @since 4.2.1
+ */
+@ContextConfiguration
+@RunWith(SpringJUnit4ClassRunner.class)
+@DirtiesContext
+public class CustomObjectNameTests {
+
+ @Autowired
+ private MBeanServer server;
+
+ @Autowired
+ private MessageHandler customHandler;
+
+ @Test
+ public void testCustomMBeanRegistration() throws Exception {
+ Set names = server.queryNames(new ObjectName("custom:type=MessageChannel,*"), null);
+ assertEquals(1, names.size());
+ ObjectName name = names.iterator().next();
+ assertEquals("custom:type=MessageChannel,name=foo", name.toString());
+ MBeanInfo mBeanInfo = server.getMBeanInfo(name);
+ assertEquals("custom channel", mBeanInfo.getDescription());
+ names = server.queryNames(new ObjectName("custom:type=MessageHandler,*"), null);
+ assertEquals(1, names.size());
+ name = names.iterator().next();
+ assertEquals("custom:type=MessageHandler,name=foo", name.toString());
+ mBeanInfo = server.getMBeanInfo(name);
+ assertEquals("custom handler", mBeanInfo.getDescription());
+ Descriptor descriptor = mBeanInfo.getDescriptor();
+ assertEquals("true", descriptor.getFieldValue("log"));
+ assertEquals("foo", descriptor.getFieldValue("logFile"));
+ assertEquals("1000", descriptor.getFieldValue("currencyTimeLimit"));
+ assertEquals("bar", descriptor.getFieldValue("persistLocation"));
+ assertEquals("baz", descriptor.getFieldValue("persistName"));
+ assertEquals("10", descriptor.getFieldValue("persistPeriod"));
+ assertEquals("Never", descriptor.getFieldValue("persistPolicy"));
+ names = server.queryNames(new ObjectName("custom:type=MessageSource,*"), null);
+ assertEquals(1, names.size());
+ name = names.iterator().next();
+ assertEquals("custom:type=MessageSource,name=foo", name.toString());
+ names = server.queryNames(new ObjectName("custom:type=MessageRouter,*"), null);
+ assertEquals(1, names.size());
+ name = names.iterator().next();
+ assertEquals("custom:type=MessageRouter,name=foo", name.toString());
+ names = server.queryNames(new ObjectName("test.custom:type=MessageHandler,*"), null);
+ assertEquals(2, names.size());
+ Iterator iterator = names.iterator();
+ name = iterator.next();
+ assertEquals("test.custom:type=MessageHandler,name=standardHandler,bean=handler", name.toString());
+ name = iterator.next();
+ assertEquals("test.custom:type=MessageHandler,name=errorLogger,bean=internal", name.toString());
+ assertTrue(AopUtils.isJdkDynamicProxy(this.customHandler));
+ }
+
+ @IntegrationManagedResource(objectName="${customChannelName}", description="custom channel")
+ public static class ChannelWithCustomObjectName extends AbstractMessageChannel {
+
+ @Override
+ protected boolean doSend(Message> message, long timeout) {
+ return false;
+ }
+
+ }
+
+ @IntegrationManagedResource(objectName="${customHandlerName}", description="custom handler",
+ currencyTimeLimit=1000, log=true, logFile="foo", persistLocation="bar", persistName="baz", persistPeriod=10,
+ persistPolicy="Never")
+ @Transactional
+ public static class HandlerWithCustomObjectName extends AbstractMessageHandler {
+
+ @Override
+ public void handleMessageInternal(Message> message) throws Exception {
+ }
+
+ }
+
+ @IntegrationManagedResource("${customSourceName}")
+ public static class SourceWithCustomObjectName extends AbstractMessageSource {
+
+ @Override
+ public String getComponentType() {
+ return "foo";
+ }
+
+ @Override
+ protected Object doReceive() {
+ return null;
+ }
+
+ }
+
+ @IntegrationManagedResource("${customRouterName}")
+ public static class RouterWithCustomObjectName extends AbstractMappingMessageRouter {
+
+ @Override
+ protected List