iT邦幫忙

2022 iThome 鐵人賽

DAY 18
0
自我挑戰組

Spring In Action系列 第 18

Create an email integration flow

  • 分享至 

  • xImage
  •  

這段以建立一個email endpoint module來示範如何建立一個integration flow。假設我們今天的商店客戶會藉由email來下訂單,那我們就很適合建立一個把email轉換成訂單並存入db的流程。

首先替email建立一個類別來在Java中操作:

package demo.email;
 
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
 
@Data
@ConfigurationProperties(prefix="demo.email")
@Component
public class EmailProperties {
  
  private String username;
  private String password;
  private String host;
  private String mailbox;
  private long pollRate = 30000;
  public String getImapUrl() {
    return String.format("imaps:/ /%s:%s@%s/%s",
        this.username, this.password, this.host, this.mailbox);
  } 
  
}

而這之中所有的properties也都可以設定在application.properties中:

demo:
  email:
    host: imap.demo.com
    mailbox: INBOX
    username: demouser
    password: 3F45d6f48
    poll-rate: 10000

不過因為是自定義的properties,所以IDE會警告說不認得這些properties,這時候可以引入一個library:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

會自動幫我們定義自定義的properties。

我們整個流程如下:

Email(IMAP) inbound channel adapter → channel → mail to order transformer → channel → Submit order outbound channel adapter

所以我們先從email adapter開始:

package demo.email;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Pollers;
import org.springframework.integration.mail.dsl.Mail;
 
@Configuration
public class OrderEmailIntegrationConfig {
  
  @Bean
  public IntegrationFlow orderEmailFlow(
      EmailProperties emailProps,
      EmailToOrderTransformer emailToOrderTransformer,
      OrderSubmitMessageHandler orderSubmitHandler) {
    
    return IntegrationFlows
        .from(Mail.imapInboundAdapter(emailProps.getImapUrl()),
            e -> e.poller(
                Pollers.fixedDelay(emailProps.getPollRate())))
        .transform(emailToOrderTransformer)
        .handle(orderSubmitHandler)
        .get();
  }
  
}

以上會用到Spring提供的Email Endpoint Module,所以需要引入以下dependency:

<dependency>
  <groupId>org.springframework.integration</groupId>
  <artifactId>spring-integration-mail</artifactId>
</dependency>

再來是emailToOrderTransformer:

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import org.apache.commons.text.similarity.LevenshteinDistance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.mail.transformer.AbstractMailMessageTransformer;
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.stereotype.Component;

@Component
public class EmailToOrderTransformer extends AbstractMailMessageTransformer<EmailOrder> {
  private static Logger logger = LoggerFactory.getLogger(EmailToOrderTransformer.class);
    
  private static final String SUBJECT_KEYWORDS = "ORDER";
 
  @Override
  protected AbstractIntegrationMessageBuilder<EmailOrder> 
                doTransform(Message mailMessage) throws Exception {
    EmailOrder emailOrder = processPayload(mailMessage);
    return MessageBuilder.withPayload(emailOrder);
  }
 
  private EmailOrder processPayload(Message mailMessage) {
    try {
      String subject = mailMessage.getSubject();
      if (subject.toUpperCase().contains(SUBJECT_KEYWORDS)) {
        String email = ((InternetAddress) mailMessage.getFrom()[0]).getAddress();
        String content = mailMessage.getContent().toString();
        return parseEmailToOrder(email, content);
      }
    } catch (MessagingException e) {
        logger.error("MessagingException: {}", e);
    } catch (IOException e) {
        logger.error("IOException: {}", e);
    }
    return null;
  }
 
  private EmailOrder parseEmailToOrder(String email, String content) {
    EmailOrder order = new EmailOrder(email);
    String[] lines = content.split("\\r?\\n");
    for (String line : lines) {
      if (line.trim().length() > 0 && line.contains(":")) {
        Product product = SomeProcessLogic(line);
        order.addProduct(product);
      }
    }
    return order;
  }

用到的EmailOrder類別:

@Data
public class EmailOrder {
  
  private final String email;
  private List<Product> products = new ArrayList<>();
 
  public void addProduct(Product product) {
    this.products.add(product);
  }
  
}

最後是submit order adapter:

import org.springframework.integration.handler.GenericHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
 
@Component
public class OrderSubmitMessageHandler
       implements GenericHandler<EmailOrder> {
 
  private RestTemplate rest;
  private ApiProperties apiProps;
 
  public OrderSubmitMessageHandler(ApiProperties apiProps, RestTemplate rest) {
    this.apiProps = apiProps;
    this.rest = rest;
  }
 
  @Override
  public Object handle(EmailOrder order, MessageHeaders headers) {
    rest.postForObject(apiProps.getUrl(), order, String.class);
    return null;
  }
}

其中用到的ApiProperties:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
 
@Data
@ConfigurationProperties(prefix = "demo.api")
@Component
public class ApiProperties {
  private String url;
}

以及自定義properties

demo:
  api:
    url: http://localhost:8081/orders/fromEmail

要注意因為我們藉由RestTemplate來發request,需引入:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

但是引入starter-web,Spring會進行auto configuration,去起tomcat出來,但是我們用不到這塊,我們只是要發request,不用去接收request,所以可以在application.properties加入:

spring:
  main:
    web-application-type: none

來把自動起tomcat的Spring MVC 自動設定關掉。


上一篇
Components of integration flow
下一篇
Reactive programming & Reactive stream specification
系列文
Spring In Action30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言