INT-937 refactoring MessageHistory, added ComponentMetadata

This commit is contained in:
Mark Fisher
2010-03-05 00:33:55 +00:00
parent b0c38e8e77
commit 30bcf6e9c7
13 changed files with 273 additions and 158 deletions

View File

@@ -31,6 +31,7 @@ import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.core.MessagingException;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.integration.message.MessageDeliveryException;
import org.springframework.integration.support.ComponentMetadata;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -54,15 +55,22 @@ public abstract class AbstractMessageChannel implements MessageChannel, BeanFact
private volatile BeanFactory beanFactory;
private final ComponentMetadata metadata = new ComponentMetadata();
private final ChannelInterceptorList interceptors = new ChannelInterceptorList();
public AbstractMessageChannel() {
this.metadata.setComponentType("channel");
}
/**
* Set the name of this channel. This will be invoked automatically whenever
* the channel is configured explicitly with a bean definition.
*/
public void setBeanName(String name) {
this.name = name;
this.metadata.setComponentName(name);
}
/**
@@ -170,7 +178,7 @@ public abstract class AbstractMessageChannel implements MessageChannel, BeanFact
Assert.notNull(message, "message must not be null");
Assert.notNull(message.getPayload(), "message payload must not be null");
message = this.convertPayloadIfNecessary(message);
message.getHeaders().getHistory().addEvent(this.getName()).setComponentType("channel");
message.getHeaders().getHistory().addEvent(this.metadata);
message = this.interceptors.preSend(message, this);
if (message == null) {
return false;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* 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.
@@ -31,6 +31,7 @@ import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.history.MessageHistory;
/**
* The headers for a {@link Message}.

View File

@@ -1,94 +0,0 @@
/*
* 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.core;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* @author Mark Fisher
* @since 2.0
*/
public class MessageHistoryEvent implements Serializable {
public static final String COMPONENT_NAME = "componentName";
public static final String COMPONENT_TYPE = "componentType";
public static final String TIMESTAMP = "timestamp";
private final Map<String, Object> properties = new HashMap<String, Object>();
public MessageHistoryEvent(String componentName) {
this.setProperty(COMPONENT_NAME, componentName);
this.setProperty(TIMESTAMP, System.currentTimeMillis());
}
public MessageHistoryEvent setComponentType(String componentType) {
this.setProperty(COMPONENT_TYPE, componentType);
return this;
}
public String getComponentType() {
return this.getProperty(COMPONENT_TYPE, String.class);
}
public String getComponentName() {
return this.getProperty(COMPONENT_NAME, String.class);
}
public long getTimestamp() {
return this.getProperty(TIMESTAMP, long.class);
}
public MessageHistoryEvent setProperty(String key, String value) {
this.properties.put(key, value);
return this;
}
public MessageHistoryEvent setProperty(String key, Number value) {
this.properties.put(key, value);
return this;
}
public MessageHistoryEvent setProperty(String key, Boolean value) {
this.properties.put(key, value);
return this;
}
public Object getProperty(String key) {
return this.properties.get(key);
}
@SuppressWarnings("unchecked")
public <T> T getProperty(String key, Class<T> type) {
Object value = this.properties.get(key);
if (value != null && type.isAssignableFrom(value.getClass())) {
return (T) value;
}
return null;
}
public String toString() {
return this.properties.toString();
}
}

View File

@@ -20,8 +20,9 @@ import java.lang.reflect.Field;
import org.springframework.core.Ordered;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageHistoryEvent;
import org.springframework.integration.message.MessageHandler;
import org.springframework.integration.support.ComponentMetadata;
import org.springframework.integration.support.ComponentMetadataProvider;
import org.springframework.util.StringUtils;
/**
@@ -36,15 +37,20 @@ class HandlerInvocationChain implements MessageHandler, Ordered {
private final MessageHandler handler;
private final String componentName;
private final String componentType;
private final ComponentMetadata metadata;
public HandlerInvocationChain(MessageHandler handler, String componentName) {
public HandlerInvocationChain(MessageHandler handler, String endpointName) {
this.handler = handler;
this.componentName = componentName;
this.componentType = determineComponentTypeFromHandlerIfPossible(handler);
if (this.handler instanceof ComponentMetadataProvider) {
this.metadata = ((ComponentMetadataProvider) this.handler).getComponentMetadata();
}
else {
this.metadata = new ComponentMetadata();
}
this.metadata.setComponentName(endpointName);
// todo move this into the handler impls componentMetadata
this.metadata.setComponentType(determineComponentTypeFromHandlerIfPossible(this.handler));
}
@@ -54,14 +60,12 @@ class HandlerInvocationChain implements MessageHandler, Ordered {
}
public void handleMessage(Message<?> message) {
MessageHistoryEvent event = message.getHeaders().getHistory().addEvent(this.componentName);
if (this.componentType != null) {
event.setComponentType(this.componentType);
if (message != null) {
message.getHeaders().getHistory().addEvent(this.metadata);
}
this.handler.handleMessage(message);
}
private static String determineComponentTypeFromHandlerIfPossible(MessageHandler handler) {
String type = null;
try {
@@ -72,7 +76,7 @@ class HandlerInvocationChain implements MessageHandler, Ordered {
}
}
catch (Exception e) {
// no COMPONENT_TYPE_LABEL avaiable
// no COMPONENT_TYPE_LABEL available
}
return type;
}

View File

@@ -19,7 +19,7 @@ package org.springframework.integration.endpoint;
import org.springframework.integration.channel.MessageChannelTemplate;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.core.MessageHistoryEvent;
import org.springframework.integration.support.ComponentMetadata;
import org.springframework.util.Assert;
/**
@@ -32,6 +32,8 @@ public abstract class MessageProducerSupport extends AbstractEndpoint {
private volatile MessageChannel outputChannel;
private final ComponentMetadata componentMetadata = new ComponentMetadata();
private final MessageChannelTemplate channelTemplate = new MessageChannelTemplate();
@@ -46,19 +48,22 @@ public abstract class MessageProducerSupport extends AbstractEndpoint {
@Override
protected void onInit() {
Assert.notNull(this.outputChannel, "outputChannel is required");
this.componentMetadata.setComponentName(this.getBeanName());
this.populateComponentMetadata(this.componentMetadata);
}
protected boolean sendMessage(Message<?> message) {
String componentName = this.getBeanName();
if (componentName != null) {
MessageHistoryEvent event = message.getHeaders().getHistory().addEvent(componentName);
this.postProcessHistoryEvent(event);
if (message != null) {
message.getHeaders().getHistory().addEvent(this.componentMetadata);
}
return this.channelTemplate.send(message, this.outputChannel);
}
protected void postProcessHistoryEvent(MessageHistoryEvent event) {
event.setComponentType("producer");
/**
* Subclasses may override this no-op method to add attributes to this
* adapter's {@link ComponentMetadata}.
*/
protected void populateComponentMetadata(ComponentMetadata metadata) {
}
}

View File

@@ -39,6 +39,7 @@ import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessagingException;
import org.springframework.integration.message.InboundMessageMapper;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.integration.support.ComponentMetadata;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -120,17 +121,19 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapper<Object[]
private final Method method;
private final String gatewayName;
private final List<MethodParameter> parameterList;
private final ComponentMetadata metadata = new ComponentMetadata();
public ArgumentArrayMessageMapper(Method method, String gatewayName) {
Assert.notNull(method, "method must not be null");
Assert.notNull(gatewayName, "gatewayName must not be null");
this.method = method;
this.gatewayName = gatewayName;
this.parameterList = this.getMethodParameterList(method);
this.metadata.setComponentName(gatewayName);
// TODO: add METHOD_NAME key for attributes?
this.metadata.setAttribute("method", this.method.getName());
}
public Message<?> toMessage(Object[] arguments) {
@@ -142,10 +145,7 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapper<Object[]
}
Message<?> message = this.mapArgumentsToMessage(arguments);
if (message != null) {
message.getHeaders().getHistory().addEvent(this.gatewayName)
.setComponentType("gateway")
// TODO: add METHOD_NAME key for props?
.setProperty("method", this.method.getName());
message.getHeaders().getHistory().addEvent(this.metadata);
}
return message;
}

View File

@@ -50,7 +50,6 @@ import org.springframework.integration.annotation.Header;
import org.springframework.integration.annotation.Headers;
import org.springframework.integration.annotation.Payload;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageHistoryEvent;
import org.springframework.integration.core.MessagingException;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.integration.util.ClassUtils;
@@ -180,14 +179,6 @@ public class MethodInvokingMessageProcessor implements MessageProcessor {
// e.g. we can invoke getValue(this.evaluationContext, message, candidate.getReturnType);
Assert.notNull(result, "Expression evaluation result was null, but this processor requires a reply.");
}
MessageHistoryEvent event = message.getHeaders().getHistory().getCurrentEvent();
if (event != null) {
String typeName = org.springframework.util.ClassUtils.getShortNameAsProperty(this.targetObject.getClass());
event.setProperty("targetType", typeName);
if (candidate.method != null) {
event.setProperty("targetMethod", candidate.method.getName());
}
}
return result;
}
catch (EvaluationException e) {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.integration.core;
package org.springframework.integration.history;
import java.io.Serializable;
import java.util.ArrayList;
@@ -24,7 +24,11 @@ import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.integration.support.ComponentMetadata;
/**
* Iterable list of {@link MessageHistoryEvent} instances.
*
* @author Mark Fisher
* @since 2.0
*/
@@ -35,29 +39,27 @@ public class MessageHistory implements Iterable<MessageHistoryEvent>, Serializab
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public MessageHistoryEvent addEvent(String componentName) {
try {
this.lock.writeLock().lock();
MessageHistoryEvent event = new MessageHistoryEvent(componentName);
this.events.add(event);
return event;
}
finally {
this.lock.writeLock().unlock();
}
}
public MessageHistoryEvent getCurrentEvent() {
try {
this.lock.readLock().lock();
int size = this.events.size();
return (size > 0) ? this.events.get(size - 1) : null;
}
finally {
this.lock.readLock().unlock();
/**
* Add a new event with the provided component metadata.
*/
public MessageHistoryEvent addEvent(ComponentMetadata metadata) {
if (metadata != null && metadata.getComponentName() != null) {
try {
this.lock.writeLock().lock();
MessageHistoryEvent event = new MessageHistoryEvent(metadata);
this.events.add(event);
return event;
}
finally {
this.lock.writeLock().unlock();
}
}
return null;
}
/**
* Returns an iterator for an unmodifiable list of the history events.
*/
public Iterator<MessageHistoryEvent> iterator() {
try {
this.lock.readLock().lock();
@@ -68,6 +70,9 @@ public class MessageHistory implements Iterable<MessageHistoryEvent>, Serializab
}
}
/**
* Returns a String representation of the history event list.
*/
public String toString() {
return this.events.toString();
}

View File

@@ -0,0 +1,49 @@
/*
* 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.history;
import org.springframework.integration.support.ComponentMetadata;
/**
* Metadata about a historically relevant messaging event along
* with a timestamp that is generated when this event is created.
*
* @author Mark Fisher
* @since 2.0
*/
public class MessageHistoryEvent extends ComponentMetadata {
public static final String TIMESTAMP = "timestamp";
/**
* Create a MessageHistoryEvent with the metadata of the source component.
*/
public MessageHistoryEvent(ComponentMetadata metadata) {
super(metadata);
this.setAttribute(TIMESTAMP, System.currentTimeMillis());
}
/**
* Returns the timestamp generated when this event was created.
*/
public long getTimestamp() {
return this.getAttribute(TIMESTAMP, long.class);
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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.support;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Metadata describing a messaging component including the
* component's name and type as well as any other attributes.
*
* @author Mark Fisher
* @since 2.0
*/
public class ComponentMetadata implements Serializable {
public static final String COMPONENT_NAME = "componentName";
public static final String COMPONENT_TYPE = "componentType";
private final Map<String, Object> attributes = new HashMap<String, Object>();
/**
* Create a new ComponentMetadata instance with no attributes.
*/
public ComponentMetadata() {
}
/**
* Create a new ComponentMetadata instance that copies all attributes
* from the provided ComponentMetadata.
*/
public ComponentMetadata(ComponentMetadata metadata) {
this.attributes.putAll(metadata.getAttributes());
}
public ComponentMetadata setComponentName(String componentName) {
this.setAttribute(COMPONENT_NAME, componentName);
return this;
}
public ComponentMetadata setComponentType(String componentType) {
this.setAttribute(COMPONENT_TYPE, componentType);
return this;
}
public String getComponentName() {
return this.getAttribute(COMPONENT_NAME, String.class);
}
public String getComponentType() {
return this.getAttribute(COMPONENT_TYPE, String.class);
}
public ComponentMetadata setAttribute(String key, String value) {
this.attributes.put(key, value);
return this;
}
public ComponentMetadata setAttribute(String key, Number value) {
this.attributes.put(key, value);
return this;
}
public ComponentMetadata setAttribute(String key, Boolean value) {
this.attributes.put(key, value);
return this;
}
public Object getAttribute(String key) {
return this.attributes.get(key);
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(String key, Class<T> type) {
Object value = this.attributes.get(key);
if (value != null && type.isAssignableFrom(value.getClass())) {
return (T) value;
}
return null;
}
public Map<String, Object> getAttributes() {
return Collections.unmodifiableMap(this.attributes);
}
public String toString() {
return this.attributes.toString();
}
}

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.support;
/**
* Interface to be implemented by messaging components
* that provide {@link ComponentMetadata}.
*
* @author Mark Fisher
* @since 2.0
*/
public interface ComponentMetadataProvider {
/**
* Returns the {@link ComponentMetadata}
*/
ComponentMetadata getComponentMetadata();
}

View File

@@ -35,9 +35,9 @@ import org.springframework.integration.channel.PollableChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.core.MessageHistoryEvent;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.BridgeHandler;
import org.springframework.integration.history.MessageHistoryEvent;
import org.springframework.integration.message.MessageHandler;
import org.springframework.integration.message.StringMessage;
import org.springframework.util.ReflectionUtils;
@@ -278,10 +278,13 @@ public class GatewayProxyFactoryBeanTests {
Iterator<MessageHistoryEvent> historyIterator = message.getHeaders().getHistory().iterator();
MessageHistoryEvent event1 = historyIterator.next();
MessageHistoryEvent event2 = historyIterator.next();
MessageHistoryEvent event3 = historyIterator.next();
assertEquals("echo", event1.getAttribute("method", String.class));
assertEquals("testGateway", event1.getComponentName());
assertEquals("echo", event1.getProperty("method", String.class));
assertEquals("channel", event2.getComponentType());
assertEquals("testChannel", event2.getComponentName());
assertEquals("bridge", event3.getComponentType());
assertEquals("testBridge", event3.getComponentName());
}