renamed modules org.springframework.integration.* -> spring-integration-*

@Ignore'd SimpleTcpNetOutboundGatewayTests#testOutboundClose() to avoid failure; this failure is correlated to the module name change, but hard to understand how it would be caused by it
This commit is contained in:
Chris Beams
2010-05-25 13:21:25 +00:00
parent b97b2fb090
commit c08a7a657e
1484 changed files with 18 additions and 23 deletions

View File

@@ -0,0 +1,249 @@
/*
* Copyright 2002-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.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.channel.DirectChannel;
import org.springframework.integration.channel.PollableChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.channel.SubscribableChannel;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.core.MessageHeaders;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.jmx.JmxHeaders;
import org.springframework.integration.jmx.OperationInvokingMessageHandler;
import org.springframework.integration.message.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.
*
* @author Mark Fisher
* @since 2.0
*/
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<String, ObjectName> exportedBeanObjectNameMap = new HashMap<String, ObjectName>();
private volatile ListableBeanFactory beanFactory;
private final Set<Class<?>> managedTypes = new HashSet<Class<?>>(
Arrays.asList(new Class<?>[] { MessageChannel.class, AbstractEndpoint.class }));
/**
* 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 void setOperationChannel(SubscribableChannel operationChannel) {
this.operationChannel = operationChannel;
}
/**
* Returns the channel to which operation-invoking Messages may be sent. Any messages
* sent to this channel must contain {@link ControlBus#TARGET_BEAN_NAME} and
* {@link JmxHeaders#OPERATION_NAME} header values, and the target bean name must
* match one that has been exported by this Control Bus. If the operation returns a
* result, the {@link MessageHeaders#REPLY_CHANNEL} header is also required.
*/
public SubscribableChannel getOperationChannel() {
return this.operationChannel;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
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<String, ?> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type);
for (Map.Entry<String, ?> 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.afterPropertiesSet();
if (this.operationChannel == null) {
this.operationChannel = new DirectChannel();
}
this.operationChannel.subscribe(handler);
}
private ObjectName generateObjectName(String beanName, Class<?> beanType) throws MalformedObjectNameException {
StringBuilder sb = new StringBuilder(this.domain + ":name=" + beanName + ",");
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));
}
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);
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();
return super.handleRequestMessage(requestMessage);
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-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.control;
/**
* This interface defines the MBean attributes and operations
* to be exposed for any MessageChannel instance.
*
* @author Mark Fisher
* @since 2.0
*/
interface MessageChannelInfo {
/**
* Returns the name of the channel.
*/
String getName();
/**
* Number of Messages that have been sent successfully.
*/
long getSendSuccessCount();
/**
* Number of Messages that have caused Exceptions on send.
* @return
*/
long getSendErrorCount();
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2002-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.control;
/**
* This interface defines the MBean attributes and operations
* to be exposed for any PollableChannel instance.
*
* @author Mark Fisher
* @since 2.0
*/
interface PollableChannelInfo extends MessageChannelInfo {
/**
* Clears all Messages currently held by the channel.
*/
void clear();
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2002-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.control;
/**
* This interface defines the MBean attributes and operations
* to be exposed for any QueueChannel instance.
*
* @author Mark Fisher
* @since 2.0
*/
interface QueueChannelInfo extends PollableChannelInfo {
/**
* Returns the number of Messages contained within the queue at the time of invocation.
*/
int getQueueSize();
/**
* Returns the remaining capacity of the queue at the time of invocation.
*/
int getRemainingCapacity();
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2002-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.jmx;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessagingException;
import org.springframework.integration.gateway.SimpleMessageMapper;
import org.springframework.integration.message.InboundMessageMapper;
import org.springframework.integration.message.MessageSource;
import org.springframework.jmx.support.ObjectNameManager;
import org.springframework.util.Assert;
/**
* A {@link MessageSource} implementation that retrieves the current
* value of a JMX attribute each time {@link #receive()} is invoked.
*
* @author Mark Fisher
* @since 2.0
*/
@SuppressWarnings("unchecked")
public class AttributePollingMessageSource implements MessageSource {
private volatile ObjectName objectName;
private volatile String attributeName;
private volatile MBeanServer server;
private volatile InboundMessageMapper<Object> mapper = new SimpleMessageMapper();
/**
* Provide the MBeanServer where the JMX MBean has been registered.
*/
public void setServer(MBeanServer server) {
this.server = server;
}
/**
* Specify the String value of the JMX MBean's {@link ObjectName}.
*/
public void setObjectName(String objectName) {
try {
this.objectName = ObjectNameManager.getInstance(objectName);
}
catch (MalformedObjectNameException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Specify the name of the attribute to be retrieved.
*/
public void setAttributeName(String attributeName) {
this.attributeName = attributeName;
}
/**
* Retrieves the attribute value and returns it in the
* payload of a Message.
*/
public Message<?> receive() {
Assert.notNull(this.server, "MBeanServer is required");
Assert.notNull(this.objectName, "object name is required");
Assert.notNull(this.attributeName, "attribute name is required");
try {
Object value = this.server.getAttribute(this.objectName, this.attributeName);
return this.mapper.toMessage(value);
}
catch (Exception e) {
throw new MessagingException("failed to retrieve JMX attribute '"
+ this.attributeName + "' on MBean [" + this.objectName + "]", e);
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2002-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.jmx;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.Notification;
import javax.management.ObjectName;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.OutboundMessageMapper;
import org.springframework.util.Assert;
/**
* Default Messaging Mapper implementation for the {@link NotificationPublishingMessageHandler}.
* If the Message has a String-typed payload, that will be passed as the 'message' of
* the Notification instance. Otherwise, the payload object will be passed as the
* 'userData' of the Notification instance.
*
* @author Mark Fisher
* @since 2.0
*/
class DefaultNotificationMapper implements OutboundMessageMapper<Notification> {
private final ObjectName sourceObjectName;
private final String defaultNotificationType;
private final AtomicLong sequence = new AtomicLong();
DefaultNotificationMapper(ObjectName sourceObjectName, String defaultNotificationType) {
this.sourceObjectName = sourceObjectName;
this.defaultNotificationType = defaultNotificationType;
}
public Notification fromMessage(Message<?> message) throws Exception {
String type = this.resolveNotificationType(message);
Assert.hasText(type,
"No notification type header is available, and no default has been provided.");
Object payload = (message != null) ? message.getPayload() : null;
String notificationMessage = (payload instanceof String) ? (String) payload : null;
Notification notification = new Notification(type, this.sourceObjectName,
this.sequence.incrementAndGet(), System.currentTimeMillis(), notificationMessage);
if (payload != null && !(payload instanceof String)) {
notification.setUserData(payload);
}
return notification;
}
private String resolveNotificationType(Message<?> message) {
String type = message.getHeaders().get(JmxHeaders.NOTIFICATION_TYPE, String.class);
return (type != null) ? type : this.defaultNotificationType;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-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.jmx;
import org.springframework.integration.core.MessageHeaders;
/**
* Constants for JMX related Message Header keys.
*
* @author Mark Fisher
* @since 2.0
*/
public abstract class JmxHeaders {
public static final String PREFIX = MessageHeaders.PREFIX + "jmx_";
public static final String OBJECT_NAME = PREFIX + "objectName";
public static final String OPERATION_NAME = PREFIX + "operationName";
public static final String NOTIFICATION_TYPE = PREFIX + "notificationType";
public static final String NOTIFICATION_HANDBACK = PREFIX + "notificationHandback";
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2002-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.jmx;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.core.Message;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.util.Assert;
/**
* A JMX {@link NotificationListener} implementation that will send Messages
* containing the JMX {@link Notification} instances as their payloads.
*
* @author Mark Fisher
* @since 2.0
*/
public class NotificationListeningMessageProducer extends MessageProducerSupport implements NotificationListener {
private final Log logger = LogFactory.getLog(this.getClass());
private volatile MBeanServer server;
private volatile ObjectName objectName;
private volatile NotificationFilter filter;
private volatile Object handback;
/**
* Provide a reference to the MBeanServer where the notification
* publishing MBeans are registered.
*/
public void setServer(MBeanServer server) {
this.server = server;
}
/**
* Specify the JMX ObjectName of the notification publisher
* to which this notification listener should be subscribed.
*/
public void setObjectName(ObjectName objectName) {
this.objectName = objectName;
}
/**
* Specify a {@link NotificationFilter} to be passed to the server
* when registering this listener. The filter may be null.
*/
public void setFilter(NotificationFilter filter) {
this.filter = filter;
}
/**
* Specify a handback object to provide context to the listener
* upon notification. This object may be null.
*/
public void setHandback(Object handback) {
this.handback = handback;
}
/**
* Notification handling method implementation. Creates a Message with the
* JMX {@link Notification} as its payload, and if the handback object is
* not null, it sets that as a Message header value. The Message is then
* sent to this producer's output channel.
*/
public void handleNotification(Notification notification, Object handback) {
if (logger.isInfoEnabled()) {
logger.info("received notification: " + notification + ", and handback: " + handback);
}
MessageBuilder<?> builder = MessageBuilder.withPayload(notification);
if (handback != null) {
builder.setHeader(JmxHeaders.NOTIFICATION_HANDBACK, handback);
}
Message<?> message = builder.build();
this.sendMessage(message);
}
@Override
public String getComponentType() {
// TODO: provide header: ("transport", "jmx");
return "notification-listener";
}
/**
* Registers the notification listener with the specified ObjectNames.
*/
@Override
protected void doStart() {
try {
Assert.notNull(this.server, "MBeanServer is required.");
Assert.notNull(this.objectName, "An ObjectName is required.");
this.server.addNotificationListener(this.objectName, this, this.filter, this.handback);
}
catch (InstanceNotFoundException e) {
throw new IllegalStateException("Failed to find MBean instance.", e);
}
}
/**
* Unregisters the notification listener.
*/
@Override
protected void doStop() {
if (this.server != null && this.objectName != null) {
try {
this.server.removeNotificationListener(this.objectName, this, this.filter, this.handback);
}
catch (InstanceNotFoundException e) {
throw new IllegalStateException("Failed to find MBean instance.", e);
}
catch (ListenerNotFoundException e) {
throw new IllegalStateException("Failed to find NotificationListener.", e);
}
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2002-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.jmx;
import java.util.Map;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.ObjectName;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.integration.core.Message;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.message.OutboundMessageMapper;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.notification.NotificationPublisher;
import org.springframework.jmx.export.notification.NotificationPublisherAware;
import org.springframework.jmx.support.ObjectNameManager;
import org.springframework.util.Assert;
/**
* @author Mark Fisher
* @since 2.0
*/
public class NotificationPublishingMessageHandler extends AbstractMessageHandler implements BeanFactoryAware, InitializingBean {
private final PublisherDelegate delegate = new PublisherDelegate();
private volatile OutboundMessageMapper<Notification> notificationMapper;
private final ObjectName objectName;
private volatile String defaultNotificationType;
public NotificationPublishingMessageHandler(ObjectName objectName) {
Assert.notNull(objectName, "JMX ObjectName is required");
this.objectName = objectName;
}
public NotificationPublishingMessageHandler(String objectName) {
Assert.notNull(objectName, "JMX ObjectName is required");
try {
this.objectName = ObjectNameManager.getInstance(objectName);
}
catch (MalformedObjectNameException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Set a mapper for creating Notifications from a Message. If not provided,
* a default implementation will be used such that String-typed payloads will be
* passed as the 'message' of the Notification and all other payload types
* will be passed as the 'userData' of the Notification.
*/
public void setNotificationMapper(OutboundMessageMapper<Notification> notificationMapper) {
this.notificationMapper = notificationMapper;
}
/**
* Specify a dot-delimited String representing the Notification type to
* use by default when <emphasis>no</emphasis> explicit Notification mapper
* has been configured. If not provided, then a notification type header will
* be required for each message being mapped into a Notification.
*/
public void setDefaultNotificationType(String defaultNotificationType) {
this.defaultNotificationType = defaultNotificationType;
}
@Override
public final void onInit() throws Exception {
Assert.isTrue(this.getBeanFactory() instanceof ListableBeanFactory, "A ListableBeanFactory is required.");
Map<String, MBeanExporter> exporters = BeanFactoryUtils.beansOfTypeIncludingAncestors(
(ListableBeanFactory) this.getBeanFactory(), MBeanExporter.class);
Assert.isTrue(exporters.size() == 1,
"No unique MBeanExporter is available in the current context (found " +
exporters.size() + ").");
MBeanExporter exporter = exporters.values().iterator().next();
if (this.notificationMapper == null) {
this.notificationMapper = new DefaultNotificationMapper(this.objectName, this.defaultNotificationType);
}
exporter.registerManagedResource(this.delegate, this.objectName);
if (this.logger.isInfoEnabled()) {
this.logger.info("Registered JMX notification publisher as MBean with ObjectName: " + this.objectName);
}
}
@Override
protected void handleMessageInternal(Message<?> message) throws Exception {
this.delegate.publish(this.notificationMapper.fromMessage(message));
}
/**
* Simple class used for the actual MBean instances to be registered.
*/
@ManagedResource
private static class PublisherDelegate implements NotificationPublisherAware {
private volatile NotificationPublisher notificationPublisher;
public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
this.notificationPublisher = notificationPublisher;
}
private void publish(Notification notification) {
Assert.state(this.notificationPublisher != null, "NotificationPublisher must not be null.");
this.notificationPublisher.sendNotification(notification);
}
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright 2002-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.jmx;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.JMException;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessagingException;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.message.MessageHandler;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.jmx.support.ObjectNameManager;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* A {@link MessageHandler} implementation for invoking JMX operations based on
* the Message sent to its {@link #handleMessage(Message)} method. Message headers
* will be checked first when resolving the 'objectName' and 'operationName' to be
* invoked on an MBean. These values would be supplied with the Message headers
* defined as {@link JmxHeaders#OBJECT_NAME} and {@link JmxHeaders#OPERATION_NAME},
* respectively. In either case, if no header is present, the value resolution
* will fallback to the defaults, if any have been configured on this instance via
* {@link #setDefaultObjectName(String)} and {@link #setDefaultOperationName(String)},
* respectively.
*
* <p>The operation parameter(s), if any, must be available within the payload of the
* Message being handled. If the target operation expects multiple parameters, they
* can be provided in either a List or Map typed payload.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @since 2.0
*/
public class OperationInvokingMessageHandler extends AbstractReplyProducingMessageHandler implements InitializingBean {
private volatile MBeanServer server;
private volatile ObjectName defaultObjectName;
private volatile String defaultOperationName;
/**
* Provide a reference to the MBeanServer within which the MBean
* target for operation invocation has been registered.
*/
public void setServer(MBeanServer server) {
this.server = server;
}
/**
* Specify a default ObjectName to use when no such header is
* available on the Message being handled.
*/
public void setDefaultObjectName(String defaultObjectName) {
try {
if (defaultObjectName != null) {
this.defaultObjectName = ObjectNameManager.getInstance(defaultObjectName);
}
}
catch (MalformedObjectNameException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Specify a default operation name to be invoked when no such
* header is available on the Message being handled.
*/
public void setDefaultOperationName(String defaultOperationName) {
this.defaultOperationName = defaultOperationName;
}
@Override
public final void onInit() {
Assert.notNull(this.server, "MBeanServer is required.");
}
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
ObjectName objectName = this.resolveObjectName(requestMessage);
String operationName = this.resolveOperationName(requestMessage);
Map<String, Object> paramsFromMessage = this.resolveParameters(requestMessage);
try {
MBeanInfo mbeanInfo = this.server.getMBeanInfo(objectName);
MBeanOperationInfo[] opInfoArray = mbeanInfo.getOperations();
boolean hasNoArgOption = false;
for (MBeanOperationInfo opInfo : opInfoArray) {
if (operationName.equals(opInfo.getName())) {
MBeanParameterInfo[] paramInfoArray = opInfo.getSignature();
if (paramInfoArray.length == 0) {
hasNoArgOption = true;
}
if (paramInfoArray.length == paramsFromMessage.size()) {
int index = 0;
Object values[] = new Object[paramInfoArray.length];
String signature[] = new String[paramInfoArray.length];
for (MBeanParameterInfo paramInfo : paramInfoArray) {
Object value = paramsFromMessage.get(paramInfo.getName());
if (value != null && value.getClass().getName().equals(paramInfo.getType())) {
values[index] = value;
signature[index] = paramInfo.getType();
index++;
}
}
if (index == paramInfoArray.length) {
return this.server.invoke(objectName, operationName, values, signature);
}
}
}
}
if (hasNoArgOption) {
return this.server.invoke(objectName, operationName, null, null);
}
throw new MessagingException(requestMessage, "failed to find JMX operation '"
+ operationName + "' on MBean [" + objectName + "] of type [" + mbeanInfo.getClassName()
+ "] with " + paramsFromMessage.size() + " parameters: " + paramsFromMessage.keySet());
}
catch (JMException e) {
throw new MessageHandlingException(requestMessage, "failed to invoke JMX operation '" +
operationName + "' on MBean [" + objectName + "]" + " with " +
paramsFromMessage.size() + " parameters: " + paramsFromMessage.keySet(), e);
}
}
/**
* First checks for the presence of a {@link JmxHeaders#OBJECT_NAME} header,
* then falls back to this handler's {@link #defaultObjectName} if available.
*/
private ObjectName resolveObjectName(Message<?> message) {
ObjectName objectName = null;
Object objectNameHeader = message.getHeaders().get(JmxHeaders.OBJECT_NAME);
if (objectNameHeader instanceof ObjectName) {
objectName = (ObjectName) objectNameHeader;
}
else if (objectNameHeader instanceof String) {
try {
objectName = ObjectNameManager.getInstance(objectNameHeader);
}
catch (MalformedObjectNameException e) {
throw new IllegalArgumentException(e);
}
}
else {
objectName = this.defaultObjectName;
}
Assert.notNull(objectName, "Failed to resolve ObjectName.");
return objectName;
}
/**
* First checks for the presence of a {@link JmxHeaders#OPERATION_NAME} header,
* then falls back to this handler's {@link #defaultOperationName} if available.
*/
private String resolveOperationName(Message<?> message) {
String operationName = message.getHeaders().get(JmxHeaders.OPERATION_NAME, String.class);
if (operationName == null) {
operationName = this.defaultOperationName;
}
Assert.notNull(operationName, "Failed to resolve operation name.");
return operationName;
}
@SuppressWarnings("unchecked")
private Map<String, Object> resolveParameters(Message<?> message) {
Map<String, Object> map = null;
if (message.getPayload() instanceof Map) {
map = (Map<String, Object>) message.getPayload();
}
else if (message.getPayload() instanceof List) {
map = this.createParameterMapFromList((List) message.getPayload());
}
else if (message.getPayload() != null && message.getPayload().getClass().isArray()) {
map = this.createParameterMapFromList(
Arrays.asList(ObjectUtils.toObjectArray(message.getPayload())));
}
else if (message.getPayload() != null) {
map = this.createParameterMapFromList(Collections.singletonList(message.getPayload()));
}
else {
map = Collections.EMPTY_MAP;
}
return map;
}
@SuppressWarnings("unchecked")
private Map<String, Object> createParameterMapFromList(List parameters) {
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 0; i < parameters.size(); i++) {
map.put("p" + (i + 1), parameters.get(i));
}
return map;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-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.jmx.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
/**
* @author Mark Fisher
* @since 2.0
*/
public class AttributePollingChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
@Override
protected String parseSource(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.integration.jmx.AttributePollingMessageSource");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "mbean-server", "server");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "object-name");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "attribute-name");
return BeanDefinitionReaderUtils.registerWithGeneratedName(
builder.getBeanDefinition(), parserContext.getRegistry());
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-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.jmx.config;
import javax.management.MBeanServerFactory;
import org.w3c.dom.Element;
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;
/**
* @author Mark Fisher
* @since 2.0
*/
public class ControlBusParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected String getBeanClassName(Element element) {
return "org.springframework.integration.control.ControlBus";
}
@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);
}
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "operation-channel");
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2002-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.jmx.config;
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
/**
* Namespace handler for Spring Integration's <em>jmx</em> namespace.
*
* @author Mark Fisher
* @since 2.0
*/
public class JmxNamespaceHandler extends AbstractIntegrationNamespaceHandler {
public void init() {
this.registerBeanDefinitionParser("operation-invoking-channel-adapter", new OperationInvokingChannelAdapterParser());
this.registerBeanDefinitionParser("attribute-polling-channel-adapter", new AttributePollingChannelAdapterParser());
this.registerBeanDefinitionParser("notification-listening-channel-adapter", new NotificationListeningChannelAdapterParser());
this.registerBeanDefinitionParser("notification-publishing-channel-adapter", new NotificationPublishingChannelAdapterParser());
this.registerBeanDefinitionParser("control-bus", new ControlBusParser());
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2002-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.jmx.config;
import org.w3c.dom.Element;
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;
/**
* @author Mark Fisher
* @since 2.0
*/
public class NotificationListeningChannelAdapterParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected String getBeanClassName(Element element) {
return "org.springframework.integration.jmx.NotificationListeningMessageProducer";
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
Object source = parserContext.extractSource(element);
String channel = element.getAttribute("channel");
if (!StringUtils.hasText(channel)) {
parserContext.getReaderContext().error("The 'channel' attribute is required.", source);
}
builder.addPropertyReference("outputChannel", channel);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "mbean-server", "server");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "notification-filter", "filter");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "handback");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "object-name");
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-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.jmx.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
/**
* @author Mark Fisher
* @since 2.0
*/
public class NotificationPublishingChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.integration.jmx.NotificationPublishingMessageHandler");
builder.addConstructorArgValue(element.getAttribute("object-name"));
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-notification-type");
return builder.getBeanDefinition();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2002-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.jmx.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
/**
* @author Mark Fisher
* @since 2.0
*/
public class OperationInvokingChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.integration.jmx.OperationInvokingMessageHandler");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "mbean-server", "server");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-object-name");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-operation-name");
return builder.getBeanDefinition();
}
}