diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java
index eacbe63662..6f25a34de6 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java
@@ -19,11 +19,9 @@ package org.springframework.integration.channel;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.core.OrderComparator;
import org.springframework.core.convert.ConversionService;
import org.springframework.integration.Message;
@@ -52,10 +50,6 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport im
private volatile boolean shouldTrack = false;
- private final AtomicLong sendSuccessCount = new AtomicLong();
-
- private final AtomicLong sendErrorCount = new AtomicLong();
-
private volatile Class>[] datatypes = new Class>[] { Object.class };
private final ChannelInterceptorList interceptors = new ChannelInterceptorList();
@@ -69,24 +63,6 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport im
this.shouldTrack = shouldTrack;
}
- /**
- * Return the current count of Messages that have been sent
- * to this channel successfully.
- */
- public long getSendSuccessCount() {
- return this.sendSuccessCount.get();
- }
-
- /**
- * Return the current count of errors that have occurred while
- * attempting to send a Message to this channel. This value is
- * incremented whenever an Exception is thrown from one of the
- * send() methods.
- */
- public long getSendErrorCount() {
- return this.sendErrorCount.get();
- }
-
/**
* Specify the Message payload datatype(s) supported by this channel. If a
* payload type does not match directly, but the 'conversionService' is
@@ -179,14 +155,10 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport im
}
try {
boolean sent = this.doSend(message, timeout);
- if (sent) {
- this.sendSuccessCount.incrementAndGet();
- }
this.interceptors.postSend(message, this, sent);
return sent;
}
catch (Exception e) {
- this.sendErrorCount.incrementAndGet();
if (e instanceof MessagingException) {
throw (MessagingException) e;
}
diff --git a/spring-integration-jmx/pom.xml b/spring-integration-jmx/pom.xml
index 20b3d6cecb..7f760d1d3c 100644
--- a/spring-integration-jmx/pom.xml
+++ b/spring-integration-jmx/pom.xml
@@ -29,5 +29,13 @@
org.springframework
spring-test
+
+ org.aspectj
+ aspectjrt
+
+
+ org.aspectj
+ aspectjweaver
+
diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/control/ControlBus.java b/spring-integration-jmx/src/main/java/org/springframework/integration/control/ControlBus.java
index d84d35c5a2..a1b4d80c23 100644
--- a/spring-integration-jmx/src/main/java/org/springframework/integration/control/ControlBus.java
+++ b/spring-integration-jmx/src/main/java/org/springframework/integration/control/ControlBus.java
@@ -16,43 +16,22 @@
package org.springframework.integration.control;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
import javax.management.MBeanServer;
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
-import org.springframework.beans.factory.BeanFactoryUtils;
-import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
-import org.springframework.context.Lifecycle;
import org.springframework.integration.Message;
-import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.channel.DirectChannel;
-import org.springframework.integration.channel.QueueChannel;
-import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.core.SubscribableChannel;
-import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.jmx.JmxHeaders;
import org.springframework.integration.jmx.OperationInvokingMessageHandler;
+import org.springframework.integration.monitor.ObjectNameLocator;
import org.springframework.integration.support.MessageBuilder;
-import org.springframework.jmx.export.MBeanExporter;
-import org.springframework.jmx.export.assembler.AbstractConfigurableMBeanInfoAssembler;
-import org.springframework.jmx.export.assembler.MBeanInfoAssembler;
-import org.springframework.jmx.support.ObjectNameManager;
import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.ObjectUtils;
/**
* JMX-based Control Bus implementation. Exports all channel and endpoint beans from a given BeanFactory as MBeans.
@@ -62,57 +41,23 @@ import org.springframework.util.ObjectUtils;
*/
public class ControlBus implements BeanFactoryAware, InitializingBean {
- public static final String DEFAULT_DOMAIN = "org.springframework.integration";
-
public static final String TARGET_BEAN_NAME = JmxHeaders.PREFIX + "_controlBus_targetBeanName";
private volatile SubscribableChannel operationChannel;
- private final MBeanExporter exporter;
-
- private final String domain;
-
- private final Map exportedBeanObjectNameMap = new HashMap();
-
- private final Map objectNameStaticProperties = new HashMap();
-
private volatile ListableBeanFactory beanFactory;
- private final Set> managedTypes = new HashSet>(Arrays.asList(new Class>[] {
- MessageChannel.class, AbstractEndpoint.class }));
+ private final ObjectNameLocator exporter;
+
+ private final MBeanServer server;
+
/**
- * Static properties that will be added to all object names.
- *
- * @param objectNameStaticProperties the objectNameStaticProperties to set
+ * Create a {@link ControlBus}.
*/
- public void setObjectNameStaticProperties(Map objectNameStaticProperties) {
- this.objectNameStaticProperties.putAll(objectNameStaticProperties);
- }
-
- /**
- * Create a {@link ControlBus} that will register channels and endpoints as MBeans with the given MBeanServer using
- * the default domain name.
- * @see #DEFAULT_DOMAIN
- */
- public ControlBus(MBeanServer server) {
- this(server, null);
- }
-
- /**
- * Create a {@link ControlBus} that will register channels and endpoints as MBeans with the given MBeanServer using
- * the specified domain name.
- */
- public ControlBus(MBeanServer server, String domain) {
- Assert.notNull(server, "MBeanServer must not be null.");
- this.domain = (domain != null) ? domain : DEFAULT_DOMAIN;
- Assert.isTrue(!ObjectUtils.containsElement(server.getDomains(), this.domain), "Domain [" + this.domain
- + "] is already in use within this MBeanServer.");
- MBeanExporter exporter = new MBeanExporter();
- exporter.setServer(server);
- exporter.setAutodetect(false);
- exporter.setAssembler(new ControlBusMBeanInfoAssembler());
- this.exporter = exporter;
+ public ControlBus(ObjectNameLocator locator, MBeanServer server) {
+ this.exporter = locator;
+ this.server = server;
}
public void setOperationChannel(SubscribableChannel operationChannel) {
@@ -133,28 +78,12 @@ public class ControlBus implements BeanFactoryAware, InitializingBean {
Assert.isTrue(beanFactory instanceof ListableBeanFactory, "A ListableBeanFactory is required.");
this.beanFactory = (ListableBeanFactory) beanFactory;
}
+
public void afterPropertiesSet() throws Exception {
- this.exporter.afterPropertiesSet();
- for (Class> type : this.managedTypes) {
- Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type);
- for (Map.Entry entry : beans.entrySet()) {
- Object bean = entry.getValue();
- String beanName = entry.getKey();
- Class> beanType = bean.getClass();
- try {
- ObjectName objectName = this.generateObjectName(beanName, beanType);
- this.exporter.registerManagedResource(bean, objectName);
- this.exportedBeanObjectNameMap.put(beanName, objectName);
- }
- catch (MalformedObjectNameException e) {
- throw new BeanInitializationException("Failed to generate JMX ObjectName.", e);
- }
- }
- }
OperationInvokingMessageHandler handler = new ControlBusOperationInvokingMessageHandler();
handler.setBeanFactory(this.beanFactory);
- handler.setServer(this.exporter.getServer());
+ handler.setServer(this.server);
handler.afterPropertiesSet();
if (this.operationChannel == null) {
this.operationChannel = new DirectChannel();
@@ -162,89 +91,13 @@ public class ControlBus implements BeanFactoryAware, InitializingBean {
this.operationChannel.subscribe(handler);
}
- protected ObjectName generateObjectName(String beanName, Class> beanType) throws MalformedObjectNameException {
-
- String name = beanName.startsWith("org.springframework.integration") ? "anonymous,generated="+beanName : beanName;
- StringBuilder sb = new StringBuilder(this.domain + ":name=" + name + ",");
- if (MessageChannel.class.isAssignableFrom(beanType)) {
- sb.append("type=channel");
- }
- else if (AbstractEndpoint.class.isAssignableFrom(beanType)) {
- sb.append("type=endpoint");
- }
- else {
- sb.append("type=" + ClassUtils.getShortNameAsProperty(beanType));
- }
- for (String key : objectNameStaticProperties.keySet()) {
- sb.append("," + key + "=" + objectNameStaticProperties.get(key));
- }
- return ObjectNameManager.getInstance(sb.toString());
-
- }
-
- /**
- * An {@link MBeanInfoAssembler} implementation for channels and endpoints.
- */
- private static class ControlBusMBeanInfoAssembler extends AbstractConfigurableMBeanInfoAssembler {
-
- @Override
- protected boolean includeOperation(Method method, String beanKey) {
- Class> declaringClass = method.getDeclaringClass();
- return this.shouldInclude(method, declaringClass);
- }
-
- @Override
- protected boolean includeReadAttribute(Method method, String beanKey) {
- Class> declaringClass = method.getDeclaringClass();
- return this.shouldInclude(method, declaringClass);
- }
-
- @Override
- protected boolean includeWriteAttribute(Method method, String beanKey) {
- Class> declaringClass = method.getDeclaringClass();
- return this.shouldInclude(method, declaringClass);
- }
-
- private boolean shouldInclude(Method method, Class> declaringClass) {
- if (MessageChannel.class.isAssignableFrom(declaringClass)
- || AbstractEndpoint.class.isAssignableFrom(declaringClass)) {
- Class> managementInterface = this.getManagementInterface(declaringClass);
- if (managementInterface != null) {
- for (Method interfaceMethod : managementInterface.getMethods()) {
- if (interfaceMethod.getName().equals(method.getName())
- && Arrays.equals(interfaceMethod.getParameterTypes(), method.getParameterTypes())) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- private Class> getManagementInterface(Class> type) {
- if (AbstractEndpoint.class.isAssignableFrom(type)) {
- return Lifecycle.class;
- }
- if (QueueChannel.class.isAssignableFrom(type)) {
- return QueueChannelInfo.class;
- }
- if (PollableChannel.class.isAssignableFrom(type)) {
- return PollableChannelInfo.class;
- }
- if (MessageChannel.class.isAssignableFrom(type)) {
- return MessageChannelInfo.class;
- }
- return null;
- }
- }
-
private class ControlBusOperationInvokingMessageHandler extends OperationInvokingMessageHandler {
@Override
protected Object handleRequestMessage(Message> requestMessage) {
String beanName = requestMessage.getHeaders().get(TARGET_BEAN_NAME, String.class);
Assert.notNull(beanName, "The ControlBus.TARGET_BEAN_NAME is required.");
- ObjectName objectName = exportedBeanObjectNameMap.get(beanName);
+ String objectName = exporter.getObjectName(beanName);
Assert.notNull(objectName, "ControlBus has not exported an MBean for '" + beanName + "'");
requestMessage = MessageBuilder.fromMessage(requestMessage).setHeader(JmxHeaders.OBJECT_NAME, objectName)
.setHeader(TARGET_BEAN_NAME, null).build();
diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/ControlBusParser.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/ControlBusParser.java
index c658ee6172..9da9311ab9 100644
--- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/ControlBusParser.java
+++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/ControlBusParser.java
@@ -18,13 +18,14 @@ package org.springframework.integration.jmx.config;
import javax.management.MBeanServerFactory;
-import org.w3c.dom.Element;
-
+import org.springframework.beans.BeanMetadataElement;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
/**
* @author Mark Fisher
@@ -39,20 +40,30 @@ public class ControlBusParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
- Object source = parserContext.extractSource(element);
- builder.getRawBeanDefinition().setSource(source);
- String mbeanServer = element.getAttribute("mbean-server");
- if (StringUtils.hasText(mbeanServer)) {
- builder.addConstructorArgReference(mbeanServer);
- }
- else {
- builder.addConstructorArgValue(MBeanServerFactory.createMBeanServer());
- }
- String domain = element.getAttribute("domain");
- if (StringUtils.hasText(domain)) {
- builder.addConstructorArgValue(domain);
- }
+ builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
+ Object mbeanServer = getMBeanServer(element, parserContext);
+ builder.addConstructorArgValue(getMBeanExporter(element, parserContext, mbeanServer));
+ builder.addConstructorArgValue(mbeanServer);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "operation-channel");
}
+ private Object getMBeanServer(Element element, ParserContext parserContext) {
+ String mbeanServer = element.getAttribute("mbean-server");
+ if (StringUtils.hasText(mbeanServer)) {
+ return new RuntimeBeanReference(mbeanServer);
+ }
+ else {
+ return MBeanServerFactory.createMBeanServer();
+ }
+ }
+
+ private BeanMetadataElement getMBeanExporter(Element element, ParserContext parserContext, Object mbeanServer) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder
+ .genericBeanDefinition("org.springframework.integration.monitor.IntegrationMBeanExporter");
+ builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "domain");
+ builder.addPropertyValue("server", mbeanServer);
+ return builder.getBeanDefinition();
+ }
+
}
diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageCumulativeHistory.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageCumulativeHistory.java
new file mode 100644
index 0000000000..a6e117ec9b
--- /dev/null
+++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageCumulativeHistory.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2009-2010 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.monitor;
+
+/**
+ * Cumulative statistics for a series of real numbers with higher weight given to recent data but without storing any
+ * history. Older values are given exponentially smaller weight, with a decay factor determined by a "window" size
+ * chosen by the client.
+ *
+ * @author Dave Syer
+ *
+ */
+public class ExponentialMovingAverageCumulativeHistory {
+
+ private int count;
+
+ private double weight;
+
+ private double sum;
+
+ private double sumSquares;
+
+ private double min;
+
+ private double max;
+
+ private final double decay;
+
+ /**
+ * @param window the exponential lapse window (number of measurements)
+ */
+ public ExponentialMovingAverageCumulativeHistory(int window) {
+ this.decay = 1 - 1. / window;
+ }
+
+ public void append(double value) {
+ if (value > max || count == 0)
+ max = value;
+ if (value < min || count == 0)
+ min = value;
+ sum = decay * sum + value;
+ sumSquares = decay * sumSquares + value * value;
+ weight = decay * weight + 1;
+ count++;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public double getMean() {
+ return weight > 0 ? sum / weight : 0.;
+ }
+
+ public double getStandardDeviation() {
+ double mean = getMean();
+ double var = weight > 0 ? sumSquares / weight - mean * mean : 0.;
+ return var > 0 ? Math.sqrt(var) : 0;
+ }
+
+ public double getMax() {
+ return max;
+ }
+
+ public double getMin() {
+ return min;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[N=%d, min=%f, max=%f, mean=%f, sigma=%f]", count, min, max, getMean(),
+ getStandardDeviation());
+ }
+
+}
diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageRateCumulativeHistory.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageRateCumulativeHistory.java
new file mode 100644
index 0000000000..99bf8b41e5
--- /dev/null
+++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageRateCumulativeHistory.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2009-2010 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.monitor;
+
+/**
+ * Cumulative statistics for rate with higher weight given to recent data but without storing any history. Older values
+ * are given exponentially smaller weight, with a decay factor determined by a duration chosen by the client.
+ *
+ * @author Dave Syer
+ *
+ */
+public class ExponentialMovingAverageRateCumulativeHistory {
+
+ private final ExponentialMovingAverageCumulativeHistory rates;
+
+ private double weight;
+
+ private double sum;
+
+ private double min;
+
+ private double max;
+
+ private volatile long t0 = System.currentTimeMillis();
+
+ private final double lapse;
+
+ private final double period;
+
+ /**
+ * @param period the period to base the rate measurement (in seconds)
+ * @param lapsePeriod the exponential lapse rate for the rate average (in seconds)
+ * @param window the exponential lapse window (number of measurements)
+ */
+ public ExponentialMovingAverageRateCumulativeHistory(double period, double lapsePeriod, int window) {
+ rates = new ExponentialMovingAverageCumulativeHistory(10);
+ this.lapse = lapsePeriod > 0 ? 0.001 / lapsePeriod : 0; // convert to millisecs
+ this.period = period * 1000; // convert to millisecs
+ }
+
+ public void increment() {
+
+ long t = System.currentTimeMillis();
+ double value = t > t0 ? (t - t0) / period : 0;
+ if (value > max || getCount() == 0) {
+ max = value;
+ }
+ if (value < min || getCount() == 0) {
+ min = value;
+ }
+ double alpha = Math.exp((t0 - t) * lapse);
+ t0 = t;
+ sum = alpha * sum + value;
+ weight = alpha * weight + 1;
+ rates.append(sum > 0 ? weight / sum : 0);
+
+ }
+
+ public int getCount() {
+ return rates.getCount();
+ }
+
+ /**
+ * @return the time in seconds since the last measurement
+ */
+ public double getTimeSinceLastMeasurement() {
+ return (System.currentTimeMillis() - t0) / 1000.;
+ }
+
+ public double getMean() {
+ int count = rates.getCount();
+ if (count==0) {
+ return 0;
+ }
+ long t = System.currentTimeMillis();
+ double value = t > t0 ? (t - t0) / period : 0;
+ return count / (count / rates.getMean() + value);
+ }
+
+ public double getStandardDeviation() {
+ return rates.getStandardDeviation();
+ }
+
+ public double getMax() {
+ return min > 0 ? 1 / min : 0;
+ }
+
+ public double getMin() {
+ return max > 0 ? 1 / max : 0;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[N=%d, min=%f, max=%f, mean=%f, sigma=%f, timeSinceLast=%f]", getCount(), getMin(),
+ getMax(), getMean(), getStandardDeviation(), getTimeSinceLastMeasurement());
+ }
+
+}
diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageRatioCumulativeHistory.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageRatioCumulativeHistory.java
new file mode 100644
index 0000000000..20b3db4135
--- /dev/null
+++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/ExponentialMovingAverageRatioCumulativeHistory.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2009-2010 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.monitor;
+
+/**
+ * Cumulative statistics for success rate (ratio) with higher weight given to recent data but without storing any
+ * history. Older values are given exponentially smaller weight, with a decay factor determined by a duration chosen by
+ * the client.
+ *
+ * @author Dave Syer
+ *
+ */
+public class ExponentialMovingAverageRatioCumulativeHistory {
+
+ private double weight;
+
+ private double sum;
+
+ private volatile long t0 = System.currentTimeMillis();
+
+ private final double lapse;
+
+ private final ExponentialMovingAverageCumulativeHistory cumulative;
+
+ /**
+ * @param lapsePeriod the exponential lapse rate for the rate average (in seconds)
+ * @param window the exponential lapse window (number of measurements)
+ */
+ public ExponentialMovingAverageRatioCumulativeHistory(double lapsePeriod, int window) {
+ this.cumulative = new ExponentialMovingAverageCumulativeHistory(window);
+ this.lapse = lapsePeriod > 0 ? 0.001 / lapsePeriod : 0; // convert to millisecs
+ }
+
+ public void success() {
+ append(1);
+ }
+
+ public void failure() {
+ append(0);
+ }
+
+ private void append(int value) {
+
+ long t = System.currentTimeMillis();
+ double alpha = Math.exp((t0 - t) * lapse);
+ t0 = t;
+ sum = alpha * sum + value;
+ weight = alpha * weight + 1;
+ cumulative.append(sum / weight);
+
+ }
+
+ public int getCount() {
+ return cumulative.getCount();
+ }
+
+ /**
+ * @return the time in seconds since the last measurement
+ */
+ public double getTimeSinceLastMeasurement() {
+ return (System.currentTimeMillis() - t0) / 1000.;
+ }
+
+ public double getMean() {
+ int count = cumulative.getCount();
+ if (count == 0) {
+ return 0;
+ }
+ long t = System.currentTimeMillis();
+ double alpha = Math.exp((t0 - t) * lapse);
+ return alpha * cumulative.getMean() + 1 - alpha;
+ }
+
+ public double getStandardDeviation() {
+ return cumulative.getStandardDeviation();
+ }
+
+ public double getMax() {
+ return cumulative.getMax();
+ }
+
+ public double getMin() {
+ return cumulative.getMin();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[N=%d, min=%f, max=%f, mean=%f, sigma=%f, timeSinceLast=%f]", getCount(), getMin(),
+ getMax(), getMean(), getStandardDeviation(), getTimeSinceLastMeasurement());
+ }
+
+}
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
new file mode 100644
index 0000000000..0e527a37f2
--- /dev/null
+++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java
@@ -0,0 +1,544 @@
+/*
+ * Copyright 2009-2010 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.monitor;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.aop.PointcutAdvisor;
+import org.springframework.aop.TargetSource;
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.Lifecycle;
+import org.springframework.context.SmartLifecycle;
+import org.springframework.integration.MessageChannel;
+import org.springframework.integration.channel.QueueChannel;
+import org.springframework.integration.core.MessageHandler;
+import org.springframework.integration.core.PollableChannel;
+import org.springframework.integration.endpoint.AbstractEndpoint;
+import org.springframework.jmx.export.MBeanExporter;
+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.naming.MetadataNamingStrategy;
+import org.springframework.jmx.support.MetricType;
+import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * MBean exporter for Spring Integration components in an existing application.
+ *
+ * @author Dave Syer
+ * @author Helena Edelson
+ */
+@ManagedResource
+public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostProcessor, BeanFactoryAware,
+ BeanClassLoaderAware, SmartLifecycle, ObjectNameLocator {
+
+ private static final Log logger = LogFactory.getLog(IntegrationMBeanExporter.class);
+
+ public static final String DEFAULT_DOMAIN = "spring.application";
+
+ private Set channelKeys = new HashSet();
+
+ private Set handlerKeys = new HashSet();
+
+ private final AnnotationJmxAttributeSource attributeSource = new AnnotationJmxAttributeSource();
+
+ private ListableBeanFactory beanFactory;
+
+ private Map