From dba742b64e9d3035d466bd7253971ad2c9c24e15 Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Mon, 2 Apr 2012 10:45:45 -0400 Subject: [PATCH] INTSAMPLES-70 - Sample showing how to process E-mail attachments For reference see: https://jira.springsource.org/browse/INTSAMPLES-70 --- intermediate/mail-attachments/README.md | 41 +++ intermediate/mail-attachments/pom.xml | 103 +++++++ .../samples/mailattachments/Main.java | 84 ++++++ .../support/EmailFragment.java | 129 +++++++++ .../support/EmailParserUtils.java | 258 ++++++++++++++++++ .../support/EmailSplitter.java | 52 ++++ .../support/EmailTransformer.java | 50 ++++ .../spring-integration-context.xml | 45 +++ .../src/main/resources/log4j.xml | 28 ++ .../MimeMessageParsingTest.java | 242 ++++++++++++++++ .../samples/mailattachments/picture.png | Bin 0 -> 10365 bytes intermediate/pom.xml | 1 + 12 files changed, 1033 insertions(+) create mode 100644 intermediate/mail-attachments/README.md create mode 100644 intermediate/mail-attachments/pom.xml create mode 100644 intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/Main.java create mode 100644 intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailFragment.java create mode 100644 intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailParserUtils.java create mode 100644 intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailSplitter.java create mode 100644 intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailTransformer.java create mode 100644 intermediate/mail-attachments/src/main/resources/META-INF/spring/integration/spring-integration-context.xml create mode 100644 intermediate/mail-attachments/src/main/resources/log4j.xml create mode 100644 intermediate/mail-attachments/src/test/java/org/springframework/integration/samples/mailattachments/MimeMessageParsingTest.java create mode 100644 intermediate/mail-attachments/src/test/resources/org/springframework/integration/samples/mailattachments/picture.png diff --git a/intermediate/mail-attachments/README.md b/intermediate/mail-attachments/README.md new file mode 100644 index 00000000..5e71a4a9 --- /dev/null +++ b/intermediate/mail-attachments/README.md @@ -0,0 +1,41 @@ +Spring Integration - Processing Email Attachments Sample +======================================================== + +# Overview + +This example demonstrates how emails including their attachments can be processed using Spring Integration. This sample uses the following Spring Integration components: + +* Mail Inbound Channel Adapter +* Chain +* Transformer +* Splitter +* File Outbound Channel Adapter + +# Getting Started + +In order to use this sample you must have access to a mail-server. You can either use an external server (e.g. GMail) or you can also easily setup your own mail server using Apache James 3.0 (http://james.apache.org/). You can find instructions for setting up a basic instance at: + +* http://james.apache.org/server/3/quick-start.html +* http://hillert.blogspot.com/2011/05/testing-email-notifications-with-apache.html + +In **src/main/resources/META-INF/spring/integration/spring-integration-context.xml** change the following to reflect the settings for your mail server. + + store-uri="imap://test:test@localhost:143/INBOX" + +Lastly, before you run the example, please make sure that your email inbox contains some messages. + +You can run this sample by either. + +* running the "Main" class from within STS (Right-click on Main class --> Run As --> Java Application) +* or from the command line: + - mvn package + - mvn exec:java + +Once started, the configured mail server will be polled for new email messages every 5 seconds. + +-------------------------------------------------------------------------------- + +For help please take a look at the Spring Integration documentation: + +http://www.springsource.org/spring-integration + diff --git a/intermediate/mail-attachments/pom.xml b/intermediate/mail-attachments/pom.xml new file mode 100644 index 00000000..504c8451 --- /dev/null +++ b/intermediate/mail-attachments/pom.xml @@ -0,0 +1,103 @@ + + 4.0.0 + org.springframework.integration.samples + mail-attachments + Samples (Intermediate) - Mail Attachment Demo + 2.2.0.BUILD-SNAPSHOT + + + UTF-8 + 2.2.0.RC1 + 1.2.17 + 4.10 + + + + 2.2.1 + + + + + org.springframework.integration + spring-integration-mail + ${spring.integration.version} + + + org.springframework.integration + spring-integration-file + ${spring.integration.version} + + + javax.activation + activation + 1.1.1 + + + javax.mail + mail + 1.4.5 + + + commons-io + commons-io + 2.4 + + + + junit + junit + ${junit.version} + test + + + org.springframework + spring-test + 3.1.1.RELEASE + test + + + org.subethamail + subethasmtp-wiser + 1.2 + test + + + + log4j + log4j + ${log4j.version} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.6 + 1.6 + -Xlint:all + true + true + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + org.springframework.integration.samples.mailattachments.Main + + + + + + + repo.springsource.org.milestone + Spring Framework Maven Milestone Repository + https://repo.springsource.org/libs-milestone + + + diff --git a/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/Main.java b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/Main.java new file mode 100644 index 00000000..795c7990 --- /dev/null +++ b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/Main.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2012 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.samples.mailattachments; + +import java.util.Scanner; + +import org.apache.log4j.Logger; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * Starts the Spring Context and will initialize the Spring Integration routes. + * + * @author Gunnar Hillert + * @since 2.2 + * + */ +public final class Main { + + private static final Logger LOGGER = Logger.getLogger(Main.class); + + private static final String HORIZONTAL_LINE = + "\n========================================================="; + + private Main() { } + + /** + * Load the Spring Integration Application Context + * + * @param args - command line arguments + */ + public static void main(final String... args) { + + LOGGER.info(HORIZONTAL_LINE + + "\n" + + "\n Welcome to Spring Integration! " + + "\n" + + "\n For more information please visit: " + + "\n http://www.springsource.org/spring-integration " + + "\n" + + HORIZONTAL_LINE ); + + final AbstractApplicationContext context = + new ClassPathXmlApplicationContext("classpath:META-INF/spring/integration/*-context.xml"); + + context.registerShutdownHook(); + + final Scanner scanner = new Scanner(System.in); + + LOGGER.info(HORIZONTAL_LINE + + "\n" + + "\n Please press 'q + Enter' to quit the application. " + + "\n" + + HORIZONTAL_LINE ); + + while (true) { + + final String input = scanner.nextLine(); + + if("q".equals(input.trim())) { + break; + } + + } + + LOGGER.info("Exiting application...bye."); + + System.exit(0); + + } +} diff --git a/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailFragment.java b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailFragment.java new file mode 100644 index 00000000..1784e2b2 --- /dev/null +++ b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailFragment.java @@ -0,0 +1,129 @@ +/* + * Copyright 2002-2012 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.samples.mailattachments.support; + +import java.io.File; + +import org.springframework.util.Assert; + +/** + * Represents a part of the original email messsage. EmailFragments could be either + * Email messages themselves or also attachments. The sample will use {@link EmailFragment}s + * to ultimately write the various pieces that constitute an email message out to + * the file system. + * + * @author Gunnar Hillert + * @since 2.2 + * + */ +public class EmailFragment { + + private Object data; + private String filename; + private File directory; + + /** + * Constructor. + * + * @param directory Must not be null + * @param filename Must not be null + * @param data Must not be null + */ + public EmailFragment(File directory, String filename, Object data) { + super(); + + Assert.notNull(directory); + Assert.hasText(filename); + Assert.notNull(data); + + this.directory = directory; + this.filename = filename; + this.data = data; + } + + /** + * The data to save to the file system, e.g. text messages/attachments, binary + * file attachments etc. + */ + public Object getData() { + return data; + } + + /** + * The file name to create for the respective {@link EmailFragment}. + */ + public String getFilename() { + return filename; + } + + /** + * The directory where to store the {@link #getData()} using the specified + * {@link #getFilename()}. + * + */ + public File getDirectory() { + return this.directory; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((directory == null) ? 0 : directory.hashCode()); + result = prime * result + + ((filename == null) ? 0 : filename.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + EmailFragment other = (EmailFragment) obj; + if (directory == null) { + if (other.directory != null) { + return false; + } + } + else if (!directory.equals(other.directory)) { + return false; + } + if (filename == null) { + if (other.filename != null) { + return false; + } + } + else if (!filename.equals(other.filename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "EmailFragment [filename=" + filename + ", directory=" + + directory + "]"; + } + +} diff --git a/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailParserUtils.java b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailParserUtils.java new file mode 100644 index 00000000..33d001a5 --- /dev/null +++ b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailParserUtils.java @@ -0,0 +1,258 @@ +/* + * Copyright 2002-2012 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.samples.mailattachments.support; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import javax.mail.BodyPart; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.Part; +import javax.mail.internet.ContentType; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.ParseException; + +import org.apache.commons.io.IOUtils; +import org.apache.log4j.Logger; +import org.springframework.util.Assert; + +/** + * Utility Class for parsing mail messages. + * + * @author Gunnar Hillert + * @since 2.2 + * + */ +public final class EmailParserUtils { + + private static final Logger LOGGER = Logger.getLogger(EmailParserUtils.class); + + /** Prevent instantiation. */ + private EmailParserUtils() { + throw new AssertionError(); + } + + /** + * Parses a mail message. The respective message can either be the root message + * or another message that is attached to another message. + * + * If the mail message is an instance of {@link String}, then a {@link EmailFragment} + * is being created using the email message's subject line as the file name, + * which will contain the mail message's content. + * + * If the mail message is an instance of {@link Multipart} then we delegate + * to {@link #handleMultipart(File, Multipart, javax.mail.Message, List)}. + * + * @param directory The directory for storing the message. If null this is the root message. + * @param mailMessage The mail message to be parsed. Must not be null. + * @param emailFragments Must not be null. + */ + public static void handleMessage(final File directory, + final javax.mail.Message mailMessage, + final List emailFragments) { + + Assert.notNull(mailMessage, "The mail message to be parsed must not be null."); + Assert.notNull(emailFragments, "The collection of emailfragments must not be null."); + + final Object content; + final String subject; + + try { + content = mailMessage.getContent(); + subject = mailMessage.getSubject(); + } catch (IOException e) { + throw new IllegalStateException("Error while retrieving the email contents.", e); + } catch (MessagingException e) { + throw new IllegalStateException("Error while retrieving the email contents.", e); + } + + final File directoryToUse; + + if (directory == null) { + directoryToUse = new File(subject); + } else { + directoryToUse = new File(directory, subject); + } + + if (content instanceof String) { + emailFragments.add(new EmailFragment(new File(subject), "message.txt", content)); + } + else if (content instanceof Multipart) { + Multipart multipart = (Multipart) content; + handleMultipart(directoryToUse, multipart, mailMessage, emailFragments); + } + else { + throw new IllegalStateException("This content type is not handled - " + content.getClass().getSimpleName()); + } + + } + + /** + * Parses any {@link Multipart} instances that contain text or Html attachments, + * {@link InputStream} instances, additional instances of {@link Multipart} + * or other attached instances of {@link javax.mail.Message}. + * + * Will create the respective {@link EmailFragment}s representing those attachments. + * + * Instances of {@link javax.mail.Message} are delegated to + * {@link #handleMessage(File, javax.mail.Message, List)}. Further instances + * of {@link Multipart} are delegated to + * {@link #handleMultipart(File, Multipart, javax.mail.Message, List)}. + * + * @param directory Must not be null + * @param multipart Must not be null + * @param mailMessage Must not be null + * @param emailFragments Must not be null + */ + public static void handleMultipart(File directory, Multipart multipart, javax.mail.Message mailMessage, List emailFragments) { + + Assert.notNull(directory, "The directory must not be null."); + Assert.notNull(multipart, "The multipart object to be parsed must not be null."); + Assert.notNull(mailMessage, "The mail message to be parsed must not be null."); + Assert.notNull(emailFragments, "The collection of emailfragments must not be null."); + + final int count; + + try { + count = multipart.getCount(); + + if (LOGGER.isInfoEnabled()) { + LOGGER.info(String.format("Number of enclosed BodyPart objects: %s.", count)); + } + + } + catch (MessagingException e) { + throw new IllegalStateException("Error while retrieving the number of enclosed BodyPart objects.", e); + } + + for (int i = 0; i < count; i++) { + + final BodyPart bp; + + try { + bp = multipart.getBodyPart(i); + } + catch (MessagingException e) { + throw new IllegalStateException("Error while retrieving body part.", e); + } + + final String contentType; + String filename; + final String disposition; + final String subject; + + try { + + contentType = bp.getContentType(); + filename = bp.getFileName(); + disposition = bp.getDisposition(); + subject = mailMessage.getSubject(); + + if (filename == null && bp instanceof MimeBodyPart) { + filename = ((MimeBodyPart) bp).getContentID(); + } + + } + catch (MessagingException e) { + throw new IllegalStateException("Unable to retrieve body part meta data.", e); + } + + if (LOGGER.isInfoEnabled()) { + LOGGER.info(String.format("BodyPart - Content Type: '%s', filename: '%s', disposition: '%s', subject: '%s'", + new Object[]{contentType, filename, disposition, subject})); + } + + if (Part.ATTACHMENT.equalsIgnoreCase(disposition)) { + LOGGER.info(String.format("Handdling attachment '%s', type: '%s'", filename, contentType)); + } + + final Object content; + + try { + content = bp.getContent(); + } + catch (IOException e) { + throw new IllegalStateException("Error while retrieving the email contents.", e); + } + catch (MessagingException e) { + throw new IllegalStateException("Error while retrieving the email contents.", e); + } + + if (content instanceof String) { + + if (Part.ATTACHMENT.equalsIgnoreCase(disposition)) { + emailFragments.add(new EmailFragment(directory, i + "-" + filename, content)); + LOGGER.info(String.format("Handdling attachment '%s', type: '%s'", filename, contentType)); + } + else { + + final String textFilename; + final ContentType ct; + + try { + ct = new ContentType(contentType); + } + catch (ParseException e) { + throw new IllegalStateException("Error while parsing content type '" + contentType + "'.", e); + } + + if ("text/plain".equalsIgnoreCase(ct.getBaseType())) { + textFilename = "message.txt"; + } + else if ("text/html".equalsIgnoreCase(ct.getBaseType())) { + textFilename = "message.html"; + } + else { + textFilename = "message.other"; + } + + emailFragments.add(new EmailFragment(directory, textFilename, content)); + } + + + } + else if (content instanceof InputStream) { + + final InputStream inputStream = (InputStream) content; + final ByteArrayOutputStream bis = new ByteArrayOutputStream(); + + try { + IOUtils.copy(inputStream, bis); + } + catch (IOException e) { + throw new IllegalStateException("Error while copying input stream to the ByteArrayOutputStream.", e); + } + + emailFragments.add(new EmailFragment(directory, filename, bis.toByteArray())); + + } + else if (content instanceof javax.mail.Message) { + handleMessage(directory, (javax.mail.Message) content, emailFragments); + } + else if (content instanceof Multipart) { + final Multipart mp2 = (Multipart) content; + handleMultipart(directory, mp2, mailMessage, emailFragments); + } + else { + throw new IllegalStateException("Content type not handled: " + content.getClass().getSimpleName()); + } + } + } +} diff --git a/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailSplitter.java b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailSplitter.java new file mode 100644 index 00000000..98a65593 --- /dev/null +++ b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailSplitter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2012 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.samples.mailattachments.support; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.integration.Message; +import org.springframework.integration.annotation.Splitter; +import org.springframework.integration.file.FileHeaders; +import org.springframework.integration.support.MessageBuilder; + +/** + * Splits a {@link List} of {@link EmailFragment}s into individual Spring Integration + * {@link Message}s. + * + * @author Gunnar Hillert + * @since 2.2 + * + */ +public class EmailSplitter { + + @Splitter + public List> splitIntoMessages(final List emailFragments) { + + final List> messages = new ArrayList>(); + + for (EmailFragment emailFragment : emailFragments) { + Message message = MessageBuilder.withPayload(emailFragment.getData()) + .setHeader(FileHeaders.FILENAME, emailFragment.getFilename()) + .setHeader("directory", emailFragment.getDirectory()) + .build(); + messages.add(message); + } + + return messages; + } + +} diff --git a/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailTransformer.java b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailTransformer.java new file mode 100644 index 00000000..dbe1599f --- /dev/null +++ b/intermediate/mail-attachments/src/main/java/org/springframework/integration/samples/mailattachments/support/EmailTransformer.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2012 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.samples.mailattachments.support; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.springframework.integration.annotation.Transformer; + +/** + * Parses the E-mail Message and converts each containing message and/or attachment into + * a {@link List} of {@link EmailFragment}s. + * + * @author Gunnar Hillert + * @since 2.2 + * + */ +public class EmailTransformer { + + private static final Logger LOGGER = Logger.getLogger(EmailTransformer.class); + + @Transformer + public List transformit(javax.mail.Message mailMessage) { + + final List emailFragments = new ArrayList(); + + EmailParserUtils.handleMessage(null, mailMessage, emailFragments); + + if (LOGGER.isInfoEnabled()) { + LOGGER.info(String.format("Email contains %s fragments.", emailFragments.size())); + } + + return emailFragments; + } + +} diff --git a/intermediate/mail-attachments/src/main/resources/META-INF/spring/integration/spring-integration-context.xml b/intermediate/mail-attachments/src/main/resources/META-INF/spring/integration/spring-integration-context.xml new file mode 100644 index 00000000..7019a6c5 --- /dev/null +++ b/intermediate/mail-attachments/src/main/resources/META-INF/spring/integration/spring-integration-context.xml @@ -0,0 +1,45 @@ + + + + + + + + + pop3 + false + + + + + + + + + + + + + + + + + diff --git a/intermediate/mail-attachments/src/main/resources/log4j.xml b/intermediate/mail-attachments/src/main/resources/log4j.xml new file mode 100644 index 00000000..a78006b5 --- /dev/null +++ b/intermediate/mail-attachments/src/main/resources/log4j.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/intermediate/mail-attachments/src/test/java/org/springframework/integration/samples/mailattachments/MimeMessageParsingTest.java b/intermediate/mail-attachments/src/test/java/org/springframework/integration/samples/mailattachments/MimeMessageParsingTest.java new file mode 100644 index 00000000..0fa4560c --- /dev/null +++ b/intermediate/mail-attachments/src/test/java/org/springframework/integration/samples/mailattachments/MimeMessageParsingTest.java @@ -0,0 +1,242 @@ +/* + * Copyright 2002-2012 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.samples.mailattachments; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; + +import org.apache.commons.io.IOUtils; +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.integration.samples.mailattachments.support.EmailFragment; +import org.springframework.integration.samples.mailattachments.support.EmailParserUtils; +import org.springframework.mail.MailParseException; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.subethamail.wiser.Wiser; +import org.subethamail.wiser.WiserMessage; + +/** + * Test to verify the correct parsing of Email Messages. + * + * @author Gunnar Hillert + * @since 2.2 + */ +public class MimeMessageParsingTest { + + private static final Logger LOGGER = Logger.getLogger(MimeMessageParsingTest.class); + + private Wiser wiser; + + @Before + public void startWiser() { + wiser = new Wiser(); + wiser.setPort(2500); + wiser.start(); + LOGGER.info("Wiser was started."); + } + + /** + * This test will create a Mime Message that contains an Attachment, send it + * to an SMTP Server (Using Wiser) and retrieve and process the Mime Message. + * + * This test verifies that the parsing of the retrieved Mime Message is + * successful and that the correct number of {@link EmailFragment}s is created. + */ + @Test + public void testProcessingOfEmailAttachments() { + + final JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + mailSender.setPort(2500); + + final MimeMessage message = mailSender.createMimeMessage(); + final String pictureName = "picture.png"; + + final ByteArrayResource byteArrayResource = getFileData(pictureName); + + try { + + final MimeMessageHelper helper = new MimeMessageHelper(message, true); + + helper.setFrom("testfrom@springintegration.org"); + helper.setTo("testto@springintegration.org"); + helper.setSubject("Parsing of Attachments"); + helper.setText("Spring Integration Rocks!"); + + helper.addAttachment(pictureName, byteArrayResource, "image/png"); + + } + catch (MessagingException e) { + throw new MailParseException(e); + } + + mailSender.send(message); + + final List wiserMessages = wiser.getMessages(); + + Assert.assertTrue(wiserMessages.size() == 1); + + boolean foundTextMessage = false; + boolean foundPicture = false; + + for (WiserMessage wiserMessage : wiserMessages) { + + final List emailFragments = new ArrayList(); + + try { + final MimeMessage mailMessage = wiserMessage.getMimeMessage(); + EmailParserUtils.handleMessage(null, mailMessage, emailFragments); + } + catch (MessagingException e) { + throw new IllegalStateException("Error while retrieving Mime Message."); + } + + Assert.assertTrue(emailFragments.size() == 2); + + for (EmailFragment emailFragment : emailFragments) { + if ("picture.png".equals(emailFragment.getFilename())) { + foundPicture = true; + } + + if ("message.txt".equals(emailFragment.getFilename())) { + foundTextMessage = true; + } + } + + Assert.assertTrue(foundPicture); + Assert.assertTrue(foundTextMessage); + + } + } + + /** + * This test will create a Mime Message that in return contains another + * mime message. The nested mime message contains an attachment. + * + * The root message consist of both HTML and Text message. + * + */ + @Test + public void testProcessingOfNestedEmailAttachments() { + + final JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + mailSender.setPort(2500); + + final MimeMessage rootMessage = mailSender.createMimeMessage(); + + try { + + final MimeMessageHelper messageHelper = new MimeMessageHelper(rootMessage, true); + + messageHelper.setFrom("testfrom@springintegration.org"); + messageHelper.setTo("testto@springintegration.org"); + messageHelper.setSubject("Parsing of Attachments"); + messageHelper.setText("Spring Integration Rocks!", "Spring Integration Rocks!"); + + final String pictureName = "picture.png"; + + final ByteArrayResource byteArrayResource = getFileData(pictureName); + + messageHelper.addInline("picture12345", byteArrayResource, "image/png"); + + } + catch (MessagingException e) { + throw new MailParseException(e); + } + + mailSender.send(rootMessage); + + final List wiserMessages = wiser.getMessages(); + + Assert.assertTrue(wiserMessages.size() == 1); + + boolean foundTextMessage = false; + boolean foundPicture = false; + boolean foundHtmlMessage = false; + + for (WiserMessage wiserMessage : wiserMessages) { + + List emailFragments = new ArrayList(); + + try { + + final MimeMessage mailMessage = wiserMessage.getMimeMessage(); + EmailParserUtils.handleMessage(null, mailMessage, emailFragments); + + } catch (MessagingException e) { + throw new IllegalStateException("Error while retrieving Mime Message."); + } + + Assert.assertTrue(emailFragments.size() == 3); + + for (EmailFragment emailFragment : emailFragments) { + if ("".equals(emailFragment.getFilename())) { + foundPicture = true; + } + + if ("message.txt".equals(emailFragment.getFilename())) { + foundTextMessage = true; + } + + if ("message.html".equals(emailFragment.getFilename())) { + foundHtmlMessage = true; + } + } + + Assert.assertTrue(foundPicture); + Assert.assertTrue(foundTextMessage); + Assert.assertTrue(foundHtmlMessage); + + } + } + + private ByteArrayResource getFileData(String filename) { + + final InputStream attachmentInputStream = MimeMessageParsingTest.class.getResourceAsStream(filename); + + Assert.assertNotNull("Resource not found: " + filename, attachmentInputStream); + + ByteArrayResource byteArrayResource = null; + + try { + byteArrayResource = new ByteArrayResource(IOUtils.toByteArray(attachmentInputStream)); + attachmentInputStream.close(); + } + catch (IOException e1) { + Assert.fail(); + } + + return byteArrayResource; + + } + + @After + public void stopWiser() { + wiser.stop(); + LOGGER.info("Wiser stopped."); + } + +} diff --git a/intermediate/mail-attachments/src/test/resources/org/springframework/integration/samples/mailattachments/picture.png b/intermediate/mail-attachments/src/test/resources/org/springframework/integration/samples/mailattachments/picture.png new file mode 100644 index 0000000000000000000000000000000000000000..01761ab05f851580417647716f20d8b12a417f87 GIT binary patch literal 10365 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 X+C#>=qSL@eK!jH^%F^YMCL#X~K0>B_ literal 0 HcmV?d00001 diff --git a/intermediate/pom.xml b/intermediate/pom.xml index a86b430a..5ff4ac00 100644 --- a/intermediate/pom.xml +++ b/intermediate/pom.xml @@ -17,6 +17,7 @@ stored-procedures-derby tcp-client-server-multiplex travel + mail-attachments