diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/MapToObjectTransformerParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/MapToObjectTransformerParser.java index a5f908ca58..1a26666c1b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/MapToObjectTransformerParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/MapToObjectTransformerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2013 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. @@ -18,13 +18,12 @@ package org.springframework.integration.config.xml; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.transformer.MapToObjectTransformer; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** * @author Oleg Zhurakousky + * @author Artem Bilan * @since 2.0 */ public class MapToObjectTransformerParser extends AbstractTransformerParser { @@ -38,17 +37,17 @@ public class MapToObjectTransformerParser extends AbstractTransformerParser { protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String ref = element.getAttribute("ref"); String type = element.getAttribute("type"); - Assert.isTrue(!(StringUtils.hasText(ref) && StringUtils.hasText(type)), - "'type' and 'ref' attributes are mutually-exclusive, but both have valid values; type: " + type + "; ref:"); - if (StringUtils.hasText(ref)){ + if (StringUtils.hasText(ref) && StringUtils.hasText(type)) { + parserContext.getReaderContext().error("'type' and 'ref' attributes are mutually-exclusive, " + + "but both have valid values; type: " + type + "; ref: " + ref, + IntegrationNamespaceUtils.createElementDescription(element)); + } + if (StringUtils.hasText(ref)) { builder.getBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(ref, "java.lang.String"); - } else if (StringUtils.hasText(type)){ - ClassLoader classLoader = parserContext.getReaderContext().getBeanClassLoader(); - if (classLoader == null) { - classLoader = this.getClass().getClassLoader(); - } - Class clazz = ClassUtils.resolveClassName(type, classLoader); - builder.getBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(clazz, "java.lang.Class"); + } + else if (StringUtils.hasText(type)) { + builder.getBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(type, "java.lang.Class"); } } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java index 15ca78ac85..2e0be55e57 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -17,7 +17,6 @@ package org.springframework.integration.expression; import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.context.expression.MapAccessor; import org.springframework.core.convert.ConversionService; @@ -32,6 +31,7 @@ import org.springframework.integration.context.IntegrationContextUtils; * * @author Gary Russell * @author Oleg Zhurakousky + * @author Artem Bilan * @since 2.2 */ public abstract class ExpressionUtils { @@ -97,10 +97,7 @@ public abstract class ExpressionUtils { * @return the evaluation context. */ public static StandardEvaluationContext createStandardEvaluationContext(BeanFactory beanFactory) { - ConversionService conversionService = IntegrationContextUtils.getConversionService(beanFactory); - if (conversionService == null && beanFactory instanceof ConfigurableListableBeanFactory){ - conversionService = ((ConfigurableListableBeanFactory)beanFactory).getConversionService(); - } - return createStandardEvaluationContext(new BeanFactoryResolver(beanFactory), conversionService); + return createStandardEvaluationContext(new BeanFactoryResolver(beanFactory), + IntegrationContextUtils.getConversionService(beanFactory)); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/MapToObjectTransformer.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/MapToObjectTransformer.java index a3d5f2268f..c71be91220 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/MapToObjectTransformer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/MapToObjectTransformer.java @@ -20,7 +20,6 @@ import java.util.Map; import org.springframework.beans.BeanUtils; import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.util.Assert; @@ -36,14 +35,15 @@ import org.springframework.validation.DataBinder; * to types that represent the properties of the Object. * * @author Oleg Zhurakousky - * @author Gunnar Hillert + * @author Artem Bilan * @since 2.0 */ -public class MapToObjectTransformer extends AbstractPayloadTransformer, Object>{ +public class MapToObjectTransformer extends AbstractPayloadTransformer, Object> { private final Class targetClass; private final String targetBeanName; + /** * @param targetClass */ @@ -52,6 +52,7 @@ public class MapToObjectTransformer extends AbstractPayloadTransformer, this.targetClass = targetClass; this.targetBeanName = null; } + /** * @param beanName */ @@ -60,31 +61,30 @@ public class MapToObjectTransformer extends AbstractPayloadTransformer, this.targetBeanName = beanName; this.targetClass = null; } - /* - * (non-Javadoc) - * @see org.springframework.integration.transformer.AbstractPayloadTransformer#transformPayload(java.lang.Object) - */ - protected Object transformPayload(Map payload) throws Exception { + + @Override + protected Object transformPayload(Map payload) throws Exception { Object target = (this.targetClass != null) ? BeanUtils.instantiate(this.targetClass) : this.getBeanFactory().getBean(this.targetBeanName); + DataBinder binder = new DataBinder(target); - ConversionService conversionService = null; - if (this.getBeanFactory() instanceof ConfigurableBeanFactory){ - conversionService = ((ConfigurableBeanFactory)this.getBeanFactory()).getConversionService(); - } - if (conversionService == null){ + ConversionService conversionService = this.getConversionService(); + if (conversionService == null) { conversionService = new DefaultConversionService(); } binder.setConversionService(conversionService); binder.bind(new MutablePropertyValues(payload)); + return target; } - protected void onInit(){ + @Override + protected void onInit() { if (StringUtils.hasText(this.targetBeanName)) { Assert.isTrue(this.getBeanFactory().isPrototype(this.targetBeanName), "target bean [" + targetBeanName + "] must have 'prototype' scope"); } } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context.xml index 25b32ad850..167cfbf1be 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context.xml @@ -9,27 +9,27 @@ - - - + - - - - + + - + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/MapToObjectTransformerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/MapToObjectTransformerTests.java index b7b5148803..b8fbe6b1fa 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/MapToObjectTransformerTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/MapToObjectTransformerTests.java @@ -20,43 +20,49 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; +import java.lang.reflect.Constructor; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; import java.util.HashMap; import java.util.Map; import org.junit.Test; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.integration.Message; +import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.util.ClassUtils; + /** * @author Oleg Zhurakousky * @author Gunnar Hillert - * + * @author Artem Bilan * @since 2.0 */ -@SuppressWarnings({ "unchecked", "rawtypes" }) public class MapToObjectTransformerTests { - @Test - public void testMapToObjectTransformation(){ - Map map = new HashMap(); + public void testMapToObjectTransformation() { + Map map = new HashMap(); map.put("fname", "Justin"); map.put("lname", "Case"); Address address = new Address(); address.setStreet("1123 Main st"); map.put("address", address); - Message message = MessageBuilder.withPayload(map).build(); + Message message = MessageBuilder.withPayload(map).build(); MapToObjectTransformer transformer = new MapToObjectTransformer(Person.class); transformer.setBeanFactory(this.getBeanFactory()); - Message newMessage = transformer.transform(message); + Message newMessage = transformer.transform(message); Person person = (Person) newMessage.getPayload(); assertNotNull(person); assertEquals("Justin", person.getFname()); @@ -67,20 +73,20 @@ public class MapToObjectTransformerTests { } @Test - public void testMapToObjectTransformationWithPrototype(){ - Map map = new HashMap(); + public void testMapToObjectTransformationWithPrototype() { + Map map = new HashMap(); map.put("fname", "Justin"); map.put("lname", "Case"); Address address = new Address(); address.setStreet("1123 Main st"); map.put("address", address); - Message message = MessageBuilder.withPayload(map).build(); + Message message = MessageBuilder.withPayload(map).build(); StaticApplicationContext ac = new StaticApplicationContext(); ac.registerPrototype("person", Person.class); MapToObjectTransformer transformer = new MapToObjectTransformer("person"); transformer.setBeanFactory(ac.getBeanFactory()); - Message newMessage = transformer.transform(message); + Message newMessage = transformer.transform(message); Person person = (Person) newMessage.getPayload(); assertNotNull(person); assertEquals("Justin", person.getFname()); @@ -91,20 +97,22 @@ public class MapToObjectTransformerTests { } @Test - public void testMapToObjectTransformationWithConversionService(){ - Map map = new HashMap(); + public void testMapToObjectTransformationWithConversionService() { + Map map = new HashMap(); map.put("fname", "Justin"); map.put("lname", "Case"); map.put("address", "1123 Main st"); - Message message = MessageBuilder.withPayload(map).build(); + Message message = MessageBuilder.withPayload(map).build(); MapToObjectTransformer transformer = new MapToObjectTransformer(Person.class); - ConfigurableBeanFactory beanFactory = this.getBeanFactory(); - ((GenericConversionService)beanFactory.getConversionService()).addConverter(new StringToAddressConverter()); + BeanFactory beanFactory = this.getBeanFactory(); + ConverterRegistry conversionService = + beanFactory.getBean(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME, ConverterRegistry.class); + conversionService.addConverter(new StringToAddressConverter()); transformer.setBeanFactory(beanFactory); - Message newMessage = transformer.transform(message); + Message newMessage = transformer.transform(message); Person person = (Person) newMessage.getPayload(); assertNotNull(person); assertEquals("Justin", person.getFname()); @@ -113,45 +121,73 @@ public class MapToObjectTransformerTests { assertEquals("1123 Main st", person.getAddress().getStreet()); } - private ConfigurableBeanFactory getBeanFactory(){ - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - GenericConversionService conversionService = new DefaultConversionService(); - beanFactory.setConversionService(conversionService); - return beanFactory; + private BeanFactory getBeanFactory() { + GenericApplicationContext ctx = TestUtils.createTestApplicationContext(); + Constructor constructorToUse = null; + try { + // Add the integrationConversionService (reflection needed because of package protection) + final Class conversionServiceCreatorClass = ClassUtils.forName("org.springframework.integration.context.ConversionServiceCreator", + ClassUtils.getDefaultClassLoader()); + constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction>() { + public Constructor run() throws Exception { + return conversionServiceCreatorClass.getDeclaredConstructor((Class[]) null); + } + }); + } + catch (Exception e) { + throw new RuntimeException("Unexpected Privilege Exception: ", e); + } + + ctx.addBeanFactoryPostProcessor((BeanFactoryPostProcessor) BeanUtils.instantiateClass(constructorToUse)); + ctx.refresh(); + return ctx; } - public static class Person{ + public static class Person { + private String fname; + private String lname; + private String ssn; + private Address address; + public String getSsn() { return ssn; } + public void setSsn(String ssn) { this.ssn = ssn; } + public String getFname() { return fname; } + public void setFname(String fname) { this.fname = fname; } + public String getLname() { return lname; } + public void setLname(String lname) { this.lname = lname; } + public Address getAddress() { return address; } + public void setAddress(Address address) { this.address = address; } } public static class Address { + private String street; public String getStreet() { @@ -163,7 +199,8 @@ public class MapToObjectTransformerTests { } } - public class StringToAddressConverter implements Converter{ + public class StringToAddressConverter implements Converter { + public Address convert(String source) { Address address = new Address(); address.setStreet(source); diff --git a/src/reference/docbook/channel.xml b/src/reference/docbook/channel.xml index 7f6a3151e4..d3a7cca06e 100644 --- a/src/reference/docbook/channel.xml +++ b/src/reference/docbook/channel.xml @@ -244,7 +244,7 @@ - + @@ -254,12 +254,12 @@ ]]> - The channel above also delegates to a queue internally, but the channel is bound + The channel above also delegates to a queue internally, but the channel is bound to the current thread, so the contents of the queue are as well. That way the thread that - sends to the channel will later be able to receive those same Messages, but no other thread - would be able to access them. While thread-scoped channels are rarely needed, they can be - useful in situations where DirectChannels are being used to enforce a - single thread of operation but any reply Messages should be sent to a "terminal" channel. + sends to the channel will later be able to receive those same Messages, but no other thread + would be able to access them. While thread-scoped channels are rarely needed, they can be + useful in situations where DirectChannels are being used to enforce a + single thread of operation but any reply Messages should be sent to a "terminal" channel. If that terminal channel is thread-scoped, the original sending thread can collect its replies from it. @@ -415,13 +415,13 @@ public Message receive(final PollableChannel channel) { ... }]]> - +
Datatype Channel Configuration - There are times when a consumer can only process a particular type of payload and you need to therefore ensure the payload type of input Messages. - Of course the first thing that comes to mind is Message Filter. However all that Message Filter will do is filter out Messages that are not compliant with - the requirements of the consumer. Another way would be to use a Content Based Router and route Messages with non-compliant data-types to specific + There are times when a consumer can only process a particular type of payload and you need to therefore ensure the payload type of input Messages. + Of course the first thing that comes to mind is Message Filter. However all that Message Filter will do is filter out Messages that are not compliant with + the requirements of the consumer. Another way would be to use a Content Based Router and route Messages with non-compliant data-types to specific Transformers to enforce transformation/conversion to the required data-type. This of course would work, but a simpler way of accomplishing the same thing is to apply the Datatype Channel pattern. You can use separate Datatype Channels for each specific payload data-type. @@ -430,10 +430,10 @@ public Message receive(final PollableChannel channel) { ... }]]>datatype attribute: - + ]]> - + Note that the type check passes for any type that is assignable to the channel's datatype. In other words, the "numberChannel" above would accept messages whose payload is java.lang.Integer or java.lang.Double. Multiple types can be @@ -443,21 +443,21 @@ public Message receive(final PollableChannel channel) { ... }]]> So the 'numberChannel' above will only accept Messages with a data-type of java.lang.Number. But what happens if the payload of the Message is not of the required type? It depends on whether you have defined a bean named "integrationConversionService" - that is an instance of Spring's Conversion Service. + that is an instance of Spring's Conversion Service. If not, then an Exception would be thrown immediately, but if you do have an "integrationConversionService" bean defined, it will be used in an attempt to convert the Message's payload to the acceptable type. You can even register custom converters. For example, let's say you are sending a Message with a String payload to the 'numberChannel' we configured above. ("5"));]]> +inChannel.send(new GenericMessage("5"));]]> - + Typically this would be a perfectly legal operation, however since we are using Datatype Channel the result of such operation would generate an exception: - @@ -467,22 +467,27 @@ All we need to do is implement a Converter. { public Integer convert(String source) { return Integer.parseInt(source); - } + } }]]> Then, register it as a Converter with the Integration Conversion Service: - + ]]> When the 'converter' element is parsed, it will create the "integrationConversionService" bean on-demand if one is not already defined. With that Converter in place, the send operation would now be successful since the Datatype Channel will use that Converter to convert the String payload to an Integer. - + + + + For more information regarding Payload Type Conversion, please read . + +
- +
QueueChannel Configuration @@ -497,9 +502,9 @@ payload to an Integer. recommended to set an explicit value for a bounded queue. - + Persistent QueueChannel Configuration - + Since a QueueChannel provides the capability to buffer Messages, but does so in-memory only by default, it also introduces a possibility that Messages could be lost in the event of a system failure. To @@ -513,35 +518,35 @@ payload to an Integer. By default any QueueChannel only stores its Messages in an in-memory Queue - and can therefore lead to the lost message scenario mentioned above. However Spring Integration + and can therefore lead to the lost message scenario mentioned above. However Spring Integration provides a JdbcMessageStore to allow a QueueChannel to be backed by an RDBMS. You can configure a Message Store for any QueueChannel by adding the message-store attribute as shown in the next example. - + ]]> - + The above example also shows that JdbcMessageStore can be configured with the namespace support provided by the Spring Integration JDBC module. All you need to do is inject any javax.sql.DataSource instance. The Spring Integration JDBC module also provides schema DDL for most popular databases. These schemas are located in the org.springframework.integration.jdbc package of that module (spring-integration-jdbc). - + One important feature is that with any transactional persistent store (e.g., JdbcMessageStore), as long as the poller has a transaction configured, a Message removed from the store will only be permanently removed if the transaction completes - successfully, otherwise the transaction will roll back and the Message will not be lost. + successfully, otherwise the transaction will roll back and the Message will not be lost. Many other implementations of the Message Store will be available as the growing number of Spring projects related to "NoSQL" data stores provide the underlying support. Of course, you can always provide your own implementation of the MessageGroupStore interface if you cannot find one that meets your particular needs.
- +
PublishSubscribeChannel Configuration @@ -648,16 +653,16 @@ payload to an Integer.
Global Channel Interceptor Configuration - + Global Channel Interceptor - + Channel Interceptors provide a clean and concise way of applying cross-cutting behavior per individual channel. If the same behavior should be applied on multiple channels, configuring the same set of interceptors for each channel would not be the most efficient way. To avoid repeated configuration while also enabling interceptors to apply to multiple channels, Spring Integration provides Global Interceptors. - + Look at the example below: @@ -680,12 +685,12 @@ payload to an Integer. ]]> A reasonable question is how will a global interceptor be injected in relation to other interceptors configured locally or through other global interceptor definitions? The current implementation provides - a very simple mechanism for defining the order of interceptor execution. + a very simple mechanism for defining the order of interceptor execution. A positive number in the order attribute will ensure interceptor injection after any existing interceptors and a negative number will ensure that the interceptor is injected before existing interceptors. This means that in the above example, the global interceptor will be injected AFTER - (since its order is greater than 0) + (since its order is greater than 0) the 'wire-tap' interceptor configured locally. If there were another global interceptor with a matching pattern, its order would be determined by comparing the values of the order attribute. To inject a global interceptor BEFORE the existing interceptors, use a negative value for the order attribute. @@ -700,7 +705,7 @@ payload to an Integer. Wire Tap As mentioned above, Spring Integration provides a simple Wire Tap interceptor out of - the box. You can configure a Wire Tap on any channel within an <interceptors/> element. + the box. You can configure a Wire Tap on any channel within an <interceptors/> element. This is especially useful for debugging, and can be used in conjunction with Spring Integration's logging Channel Adapter as follows: @@ -710,23 +715,23 @@ payload to an Integer. ]]> - The 'logging-channel-adapter' also accepts an 'expression' attribute so that you can evaluate - a SpEL expression against 'payload' and/or 'headers' variables. Alternatively, to simply log + The 'logging-channel-adapter' also accepts an 'expression' attribute so that you can evaluate + a SpEL expression against 'payload' and/or 'headers' variables. Alternatively, to simply log the full Message toString() result, provide a value of "true" for the 'log-full-message' attribute. That is false by default so that only the payload is logged. Setting that to true enables logging of all headers in addition to the payload. The 'expression' option does provide the most flexibility, however (e.g. expression="payload.user.name"). - + A little more on Wire Tap - One of the common misconceptions about the wire tap and other similar components () + One of the common misconceptions about the wire tap and other similar components () is that they are automatically asynchronous in nature. Wire-tap as a component is not invoked asynchronously be default. Instead, Spring Integration focuses on a single unified - approach to configuring asynchronous behavior: the Message Channel. + approach to configuring asynchronous behavior: the Message Channel. What makes certain parts of the message flow sync or async is the type of Message Channel that has been configured within that flow. That @@ -736,7 +741,7 @@ payload to an Integer. just an internal, implicit realization of the EIP pattern, it is fully exposed as a configurable component to the end user. - So, the Wire-tap component is ONLY responsible for performing the following 3 tasks: + So, the Wire-tap component is ONLY responsible for performing the following 3 tasks: intercept a message flow by tapping into a channel (e.g., channelA) @@ -751,21 +756,21 @@ payload to an Integer. It is essentially a variation of the Bridge, but it is encapsulated within a channel definition (and hence easier to enable and disable without disrupting a flow). Also, unlike the bridge, it - basically forks another message flow. Is that flow synchronous or + basically forks another message flow. Is that flow synchronous or asynchronous? The answer simply depends on the type of Message Channel that 'channelB' is. And, now you know that we have: Direct Channel, Pollable Channel, and Executor Channel as options. The last two do break the thread boundary making communication via such channels asynchronous simply because the dispatching of the message from that channel to its subscribed handlers happens on a different thread than the one used to send the message to that - channel. That is what is going to make your wire-tap flow sync or async. - It is consistent with other components within the framework (e.g., Message Publisher) and actually + channel. That is what is going to make your wire-tap flow sync or async. + It is consistent with other components within the framework (e.g., Message Publisher) and actually brings a level of consistency and simplicity by sparing you from worrying in advance (other than writing - thread safe code) whether a particular piece of code should be implemented as sync or - async. The actual wiring of two pieces of code (component A and component B) via - Message Channel is what makes their collaboration sync or + thread safe code) whether a particular piece of code should be implemented as sync or + async. The actual wiring of two pieces of code (component A and component B) via + Message Channel is what makes their collaboration sync or async. You may even want to change from sync to - async in the future and Message Channel is what's going + async in the future and Message Channel is what's going to allow you to do it swiftly without ever touching the code. One final point regarding the Wire Tap is that, despite the rationale provided above for not @@ -784,13 +789,13 @@ payload to an Integer. It is possible to configure a global wire tap as a special case of the . Simply configure a top level wire-tap element. Now, in addition to the normal wire-tap namespace support, the pattern and order attributes are supported and work in exactly the same way as with the channel-interceptor ]]> - A global wire tap provides a convenient way to configure a single channel wire tap externally without modifying the existing channel configuration. Simply set the pattern attribute to the target channel name. For example, This technique may be used to configure a test case to verify messages on a channel. + A global wire tap provides a convenient way to configure a single channel wire tap externally without modifying the existing channel configuration. Simply set the pattern attribute to the target channel name. For example, This technique may be used to configure a test case to verify messages on a channel.
- +
- +
Special Channels @@ -798,7 +803,7 @@ payload to an Integer. If namespace support is enabled, there are two special channels defined within the application context by default: errorChannel and nullChannel. The 'nullChannel' acts like /dev/null, simply logging any Message sent to it at DEBUG level and returning immediately. Any time you face channel - resolution errors for a reply that you don't care about, you can set the affected component's output-channel attribute + resolution errors for a reply that you don't care about, you can set the affected component's output-channel attribute to 'nullChannel' (the name 'nullChannel' is reserved within the application context). The 'errorChannel' is used internally for sending error messages and may be overridden with a custom configuration. This is discussed in greater detail in . diff --git a/src/reference/docbook/endpoint.xml b/src/reference/docbook/endpoint.xml index 741ccf8b8c..8f44e4dae5 100644 --- a/src/reference/docbook/endpoint.xml +++ b/src/reference/docbook/endpoint.xml @@ -586,7 +586,8 @@ any transaction configuration essentially allowing you to enhance the behavior o be mapped to a Message payload or part of the payload or header (when using the Spring Expression Language). However there are times when the type of input parameter of the endpoint method does not match the type of the payload or its part. In this scenario we need to perform type conversion. Spring Integration provides a convenient way for registering type - converters (using the Spring 3.x ConversionService) within its own instance of a conversion service bean named integrationConversionService. + converters (using the Spring 3.x ConversionService) within its own instance of a conversion service bean named + integrationConversionService. That bean is automatically created as soon as the first converter is defined using the Spring Integration namespace support. To register a Converter all you need is to implement @@ -594,13 +595,38 @@ any transaction configuration essentially allowing you to enhance the behavior o convenient namespace support: - ]]> +]]> or as an inner bean: - - ]]> + +]]> + + + When configuring an Application Context, the + Spring Framework allows you to add a conversionService bean + (see + Configuring a ConversionService chapter). This service is used, when needed, + to perform appropriate conversions during bean creation and configuration. + + + In contrast, the integrationConversionService is used for runtime conversions. + These uses are quite different; converters that are intended for use when wiring bean + constructor-args and properties may produce unintended results if used at runtime + for Spring Integration expression evaluation against + Messages within Datatype Channels, Payload Type transformers etc. + + + However, if you do want to use the Spring conversionService as + the Spring Integration integrationConversionService, + you can configure an alias in the Application Context: + ]]> + In this case the conversionService's Converters will be available for Spring Integration + runtime conversion. + + +
@@ -620,7 +646,7 @@ any transaction configuration essentially allowing you to enhance the behavior o forum (http://forum.springsource.org/showthread.php?t=94519): - + ]]>