Spring Integration Study memorandum ~ Understanding Spring Integration Sample 3. Enricher ~

Purpose

In order to learn Spring Integration, I understood the sample provided by Spring Project and reimplemented it + arranged it. This article is a memorandum. So I think there are various mistakes, but I would appreciate it if you could point out that point. The sample is registered in the following Git. spring-integration-sample

This time about basic _ ** Enricher ** _. Continuing from JMS Gateway, I thought I could do it with JMS aggregator, but before doing JMS or other Adapter system, it is basic. When I thought about doing some kind of component, this sample program was not just an enricher, but it seemed to be basic with a normal gateway``` tag, so I decided to do this first. By the way, the version of Spring Integration is `4.3.10.RELEASE```.

Overview

The concept of enricher is simple: add some information to the message header or payload along the way. Since the sample app only adds information to the payload, I've implemented something that adds information to the header as well.

The flow of the application is as follows. AP start-> Enter any of 1 to 3 on the console and press Enter (AP stop in case of `` `q```)-> 1 to 4 will give different information. Output the given result to the console. 1 to 4 are as follows.

  1. Enter a value in the User object that is Payload and display it. 2.1 Payload is now User.username
  2. Add items to the Payload Map and display them.
  3. Add attributes to Haeder and display them.

Implementation

Overall picture

The overall flow looks like the following.

requestchannel(gateway) -> finduserenricherchannel -> serviceactivator -> finduserservicechannel -> replychannel(gateway) ->Standard output(Output value in main class)

First, `getBean``` the service interface defined by the ``` gateway``` tag in the Main class. Then, the method to be called is changed according to the input value from the console. The called method is linked with the gateway``` tag, and `` <int:according to the channel name specified by the value of the ``<int: method> `` tag. It is a flow to delegate the process to the enricher> tag ``` → <int: service-activator> ``.

messagechannel -> headerenricher -> pollablechannel ->Standard output(Output value in main class)

HeaderEnricher didn't know how to implement with gateway tag, so in Main class, `` getBean``` for send channel and receive channel respectively, send and receive. It is the most basic implementation to retrieve the value.

bean definition file

The structure of the sample program is composed of three parts (gateway, enricher, service-activator), but since it is difficult on Qiita if the bean definition is long, service-activator was implemented with annotations this time. 1 is a component scan for that service-activator. 2 is the bean definition of the channel to send to each enricher when routing the process with gateway. 3 is the setting of gateway, interface is defined by `` `service-interface```, and the definition is that the channel to be sent is changed for each method in the child element.

    <!-- 1 -->
    <context:component-scan base-package="org.ek.sample"/>
    
    <!-- 2 -->
    <int:channel id="findUserEnricherChannel"/>
    <int:channel id="findUserByUsernameEnricherChannel"/>
    <int:channel id="findUserWithMapEnricherChannel"/>
    <int:channel id="tryHeaderEnricherChannel"/>
    <int:channel id="requestChannel"/>
    <int:channel id="replyChannel"/>
         
	<!-- 3 -->
	<int:gateway id="gateway"
		default-request-timeout="5000"
		default-reply-timeout="5000"
		default-request-channel="requestChannel"
		default-reply-channel="replyChannel"
		service-interface="org.ek.sample.service.UserService">
		<int:method name="findUser"                  request-channel="findUserEnricherChannel"/>
        <int:method name="findUserByUsername"        request-channel="findUserByUsernameEnricherChannel"/>
        <int:method name="findUserWithUsernameInMap" request-channel="findUserWithMapEnricherChannel"/>
    </int:gateway>

In 4, it means that the processing is entrusted to the method (`service-activator```) that inputs the channel of the ``` request-channel``` attribute. Also, among the methods specified in the service-interface``` attribute of the `` gatewaytag, the method to be delegated from here has an argument oforg.ek.sample.domain.User. Since it is a bean file called `` and there are no other attribute settings, _serviceActivator_ needs to define methods accordingly. Next, the child element ``` <int: property> `` represents the return type org.ek.sample.domain.User, of which email and password It means that only the attribute adopts the content of enricher. In other words, in this setting, no matter how many other items are manipulated with enricher, it will not be reflected in the value to be replied.

    <int:channel id="findUserServiceChannel"/>
    <int:channel id="findUserByUsernameServiceChannel"/>

    <!-- 4 -->
    <int:enricher id="findUserEnricher"
                  input-channel="findUserEnricherChannel"
                  request-channel="findUserServiceChannel">
        <int:property name="email" expression="payload.email"/>
        <int:property name="password" expression="payload.password"/>
    </int:enricher>

    <!-- 5 -->
    <int:enricher id="findUserByUsernameEnricher"
                  input-channel="findUserByUsernameEnricherChannel"
                  request-channel="findUserByUsernameServiceChannel"
                  request-payload-expression="payload.username">
        <int:property name="email" expression="payload.email"/>
        <int:property name="password" expression="payload.password"/>
    </int:enricher>

    <!-- 6 -->
    <int:enricher id="findUserWithMapEnricher"
                  input-channel="findUserWithMapEnricherChannel"
                  request-channel="findUserByUsernameServiceChannel"
                  request-payload-expression="payload.username">
        <int:property name="user" expression="payload"/>
    </int:enricher>
    
    <int:channel id="tryHeaderEnricherPollarChannel" >
    	<int:queue capacity="10"/>
    </int:channel>
    
    <!-- 7 -->
    <int:header-enricher id="tryHeaderEnricher" 
    					input-channel="tryHeaderEnricherChannel" 
    					output-channel="tryHeaderEnricherPollarChannel">    			
    					<int:header name="headerTest" value="test" />		
    					<int:header name="addedHeader" ref="stringConv" method="upperCase" />
    </int:header-enricher>
    <bean id="stringConv" class="org.ek.sample.domain.StringConverter"/>

5 is almost the same as 4, but the only difference is that `<request-payload-expression>` is set. Since the argument of the serviceActivator method specified by this enricher is a String, here it means that the item named username of payload is linked to serviceActivator as an argument.

Also, 6 seems to be the same as 5 at first glance, but in 6 it is the root when calling the method with the argument Map in `` `service-interface```, and here unlike 5, Map If there is an item called username in key, it means that it is linked to the method (same as 5) argument of serviceActivator.

7 is headerEnricher, which gives two attributes to MessageHeaders that came from the simple input_channel, and the second one uses a bean file reference (ref). Expressed. I created a simple class that just returns the referenced class in uppercase.

service-interface This is the interface defined by the `` `gateway``` tag.

public interface UserService {
	
	User findUser(User user);
	
	User findUserByUsername(User user);
	
	Map<String, Object> findUserWithUsernameInMap(Map<String, Object> userdata);

}

serviceActivator In fact, the Enricher role class is defined as serviceActivator. As mentioned in xml, the first method has an argument type of org.ek.sample.domain.User, and the second method has an argument type of String. ..

@MessageEndpoint
@EnableIntegration
public class UserServiceEnricher {
	
	private static final Logger LOGGER = Logger.getLogger(UserServiceEnricher.class);
	
	@ServiceActivator(inputChannel="findUserServiceChannel")
	public User findUser(User user) {

		LOGGER.info(String.format("Calling method 'findUser' with parameter %s", user));
		return new User(user.getUsername() + "-test",//The character string added by username is not reflected
				   "secret",
				   user.getUsername() + "@springintegration.org");
	}



	@ServiceActivator(inputChannel="findUserByUsernameServiceChannel")
	public User findUserByUsername(String username) {

		LOGGER.info(String.format("Calling method 'findUserByUsername' with parameter: %s", username));

		return new User(username, "secret", username + "@springintegration.org");

	}

Main class

I will omit the parts other than the important ones, but they are as follows.

	User user = new User();

			if ("1".equals(input)) {

				final User fullUser = service.findUser(user);
				printUserInformation(fullUser);

			} else if ("2".equals(input)) {

				final User fullUser = service.findUserByUsername(user);
				printUserInformation(fullUser);

			} else if ("3".equals(input)) {

                // 3
				final Map<String, Object> userData = new HashMap<String, Object>();
				userData.put("username", "foo_map");
				userData.put("username2", "bar_map");
				final Map<String, Object> enrichedUserData = service.findUserWithUsernameInMap(userData);
				printUserFullInformation(enrichedUserData);

			} else if("4".equals(input)){
              // 4
				MessageChannel mc = context.getBean("tryHeaderEnricherChannel", MessageChannel.class);
				PollableChannel pc = context.getBean("tryHeaderEnricherPollarChannel", PollableChannel.class);
				mc.send(new GenericMessage<String>("foo.bar"));
				printHeaderInfo(pc.receive().getHeaders());				
				
			} else{			
				LOGGER.info("\n\n    Please enter '1', '2', or '3' <enter>:\n\n");
			}	
            
            
            
            // omitted
            
            
                // 5
            	private static void printUserFullInformation(Map<String, Object> userdataMap) {
		for(Map.Entry<String, Object> entry : userdataMap.entrySet()) {
			Object val = entry.getValue();
			if(val instanceof User) {
				User user = (User) entry.getValue();
				LOGGER.info(String.format("\n\n    User found - Key of Map: '%s',  Username: '%s',  Email: '%s', Password: '%s'.\n\n",
						entry.getKey(), user.getUsername(), user.getEmail(), user.getPassword()));
			}else {
				LOGGER.info(String.format("\n\n    User found - Key of Map: '%s',  Username: '%s',  Email: '%s', Password: '%s'.\n\n",
						entry.getKey(), val, null, null));
			}
		}
	}
    
    // 6
    private static void printHeaderInfo(MessageHeaders mh) {
		LOGGER.info("\n\n    " + "headerTest :" + mh.get("headerTest") + ", addedHeader :" + mh.get("addedHeader"));
	}

The part 3 is slightly changed, and the key-values username and username2 are put into the argument Map to make it an argument. Also, all the return values are displayed with _map.entrySet () _ (5), but as the result below shows, it seems that it will be one element created with the map element specified in the argument + enricher. is there. In addition, the key name is user defined by ``` <int: property>` `` in xml, and since payload was specified in the value attribute in xml, the User object becomes the value of Map as it is.

0:10:42.631 INFO  [main][org.ek.sample.Main] 

    User found - Key of Map: 'username2',  Username: 'bar_map',  Email: 'null', Password: 'null'.


00:10:42.632 INFO  [main][org.ek.sample.Main] 

    User found - Key of Map: 'user',  Username: 'foo_map',  Email: 'secret', Password: '[email protected]'.


00:10:42.633 INFO  [main][org.ek.sample.Main] 

    User found - Key of Map: 'username',  Username: 'foo_map',  Email: 'null', Password: 'null'.

4 is headerEnricher, and if you retrieve MessageHeader according to the method, the attribute will be added to header as specified in xml. The reason why only header is implemented like this is that, as mentioned above, the default of gateway is to return payload, so it was not possible to get the value of header like enricher for payload. However, that alone seems to be awkward to use, and I thought that I could somehow set the header to be enriched with the gateway tag, but I will investigate it when I have time. The following is the display result of 6.

00:16:42.883 INFO  [main][org.ek.sample.Main] 

    headerTest :test, addedHeader :FOO.BAR

This time, I used the `` gateway``` tag, which is not an adapter, for the first time, so I felt that I had somehow understood the basics of integration. By the way, in what order should the samples be implemented in order to effectively learn spring integration?

The code implemented this time is as follows. Tweaked sample

Recommended Posts

Spring Integration Study memorandum ~ Understanding Spring Integration Sample 3. Enricher ~
Spring Integration Study memorandum ~ Understanding Spring Integration Sample 1. Hello World ~
Spring Integration Study memorandum ~ Understanding Spring Integration Sample 2. JMS Gateway ~
WebMvcConfigurer Memorandum of Understanding for Spring Boot 2.0 (Spring 5)
Java study memorandum
Spring Boot Memorandum
Memorandum (Spring Web)
Javasilver study session memorandum