這段以建立一個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 自動設定關掉。