Skip to content
This repository has been archived by the owner on Apr 5, 2022. It is now read-only.

Commit

Permalink
XD-2076 & XD-2498 & XD-3076 : Add SSL and attachments to mail sink
Browse files Browse the repository at this point in the history
  • Loading branch information
fmarchand authored and krqnf committed Nov 11, 2015
1 parent b7b5bec commit f8b75ce
Show file tree
Hide file tree
Showing 12 changed files with 392 additions and 39 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ ext {
equalsverifierVersion = '1.1.3'
ftpServerVersion = '1.0.6'
apacheSshdVersion = '0.10.1'
greenmailVersion = '1.3.1b'
greenmailVersion = '1.4.1'
httpClientVersion = '4.2.5'
jcloudsVersion = '1.7.0'
oracleToolsVersion = '1.2.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@

import org.springframework.xd.module.options.spi.Mixin;
import org.springframework.xd.module.options.spi.ModuleOption;

import org.springframework.xd.module.options.spi.ProfileNamesProvider;

/**
* Captures options for the {@code mail} sink module.
*
* @author Eric Bottard
* @author Franck Marchand
*/
@Mixin(MailServerMixin.class)
public class MailSinkOptionsMetadata {
public class MailSinkOptionsMetadata implements ProfileNamesProvider {

public static final String WITH_ATTACHMENT = "with-attachment";
public static final String WITHOUT_ATTACHMENT = "without-attachment";

private String bcc = "null";

Expand All @@ -44,6 +48,61 @@ public class MailSinkOptionsMetadata {

private String to = "null";

private boolean auth = false;

private boolean starttls = false;

private boolean ssl = false;

private String attachmentExpression;

private String attachmentFilename;

public String getAttachmentExpression() {
return attachmentExpression;
}

@ModuleOption("file uri to attach to the mail")
public void setAttachmentExpression(String attachmentExpression) {
this.attachmentExpression = attachmentExpression;
}

public String getAttachmentFilename() {
return attachmentFilename;
}

@ModuleOption("name of the attachment that will appear in the mail")
public void setAttachmentFilename(String attachmentFilename) {
this.attachmentFilename = attachmentFilename;
}

public boolean isAuth() {
return auth;
}

@ModuleOption("enable authentication for mail sending connection")
public void setAuth(boolean auth) {
this.auth = auth;
}

public boolean isSsl() {
return ssl;
}

@ModuleOption("enable ssl for mail sending connection")
public void setSsl(boolean ssl) {
this.ssl = ssl;
}

public boolean isStarttls() {
return starttls;
}

@ModuleOption("enable ttl for mail sending connection")
public void setStarttls(boolean starttls) {
this.starttls = starttls;
}

// @NotNull as a String, but the contents can be the String
// "null", which is a SpEL expression in its own right.
@NotNull
Expand Down Expand Up @@ -71,7 +130,6 @@ public String getReplyTo() {
return replyTo;
}


@NotNull
public String getSubject() {
return subject;
Expand Down Expand Up @@ -117,5 +175,10 @@ public void setTo(String to) {
this.to = to;
}

@Override
public String[] profilesToActivate() {
return new String[] { (attachmentExpression != null && attachmentFilename != null) ? WITH_ATTACHMENT : WITHOUT_ATTACHMENT };
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2013-2014 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.xd.mail;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.mail.MailHeaders;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.messaging.Message;
import org.springframework.util.StringUtils;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.nio.file.Paths;

/**
* This transformer can handle ssl, tls and attachments for
* the mail sink.
*
* @author Franck Marchand
*/
public class MailTransformer {
public static final String MAIL_ATTACHMENT = "mail_attachment";

@Autowired
private JavaMailSenderImpl sender;

public Message<String> sendMail(final Message<String> msg) {

MimeMessage mimeMsg = sender.createMimeMessage();

String subject = (String) msg.getHeaders().get(MailHeaders.SUBJECT);
final String validSubject = subject!=null ? subject : "";
final String to = (String) msg.getHeaders().get(MailHeaders.TO);
final String cc = (String) msg.getHeaders().get(MailHeaders.CC);
final String bcc = (String) msg.getHeaders().get(MailHeaders.BCC);
final String from = (String) msg.getHeaders().get(MailHeaders.FROM);
final String replyTo = (String) msg.getHeaders().get(MailHeaders.REPLY_TO);
final String attachmentFilename = (String) msg.getHeaders().get(MailHeaders.ATTACHMENT_FILENAME);
final String attachment = (String) msg.getHeaders().get(MAIL_ATTACHMENT);

try {
sender.send(new MimeMessagePreparator() {
@Override
public void prepare(MimeMessage mimeMessage) throws Exception {
MimeMessageHelper mMsg = new MimeMessageHelper(mimeMessage, true);

mMsg.setTo(to);
mMsg.setFrom(from);
mMsg.setReplyTo(replyTo);
mMsg.setSubject(validSubject);

if (bcc != null)
mMsg.setBcc(bcc);
if (cc != null)
mMsg.setCc(cc);

mMsg.setText(msg.getPayload());

if (attachment != null && attachmentFilename != null) {
String[] attachments;
if(attachment.contains(";"))
attachments = StringUtils.split(attachment, ";");
else attachments = new String[] { attachment };

String[] attachmentFilenames;
if(attachmentFilename.contains(";"))
attachmentFilenames = StringUtils.split(attachmentFilename, ";");
else attachmentFilenames = new String[] { attachmentFilename };

for(int i=0; i<attachments.length;i++) {
try {
mMsg.addAttachment(attachmentFilenames[i], Paths.get(attachments[i]).toFile());
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
}
});
} catch (MailException e) {
e.printStackTrace();
}

return msg;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright 2013-2014 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.xd.mail;

import com.icegreen.greenmail.util.DummySSLSocketFactory;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetup;
import com.icegreen.greenmail.util.ServerSetupTest;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.xd.dirt.server.singlenode.SingleNodeApplication;
import org.springframework.xd.dirt.test.SingleNodeIntegrationTestSupport;
import org.springframework.xd.dirt.test.SingletonModuleRegistry;
import org.springframework.xd.dirt.test.process.SingleNodeProcessingChainProducer;
import org.springframework.xd.dirt.test.process.SingleNodeProcessingChainSupport;
import org.springframework.xd.module.ModuleType;
import org.springframework.xd.test.RandomConfigurationSupport;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMultipart;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.Security;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

/**
* @author Franck MARCHAND
*/
public class MailSinkIntegrationTest {
public static final int TIMEOUT = 5000;
private static SingleNodeApplication application;

private static GreenMail greenMail = new GreenMail(new ServerSetup[] { ServerSetupTest.SMTPS, ServerSetupTest.SMTP, ServerSetupTest.IMAPS });

@BeforeClass
public static void setUp() {
Security.setProperty("ssl.SocketFactory.provider", DummySSLSocketFactory.class.getName());
greenMail.setUser("[email protected]", "[email protected]", "test1");
greenMail.start();

new RandomConfigurationSupport();
application = new SingleNodeApplication().run();
SingleNodeIntegrationTestSupport singleNodeIntegrationTestSupport = new SingleNodeIntegrationTestSupport(application);
singleNodeIntegrationTestSupport.addModuleRegistry(new SingletonModuleRegistry(ModuleType.sink, "mail"));
}

@Test
public void testSSLMailSinkWithAttachmentsIntegration() throws IOException, InterruptedException, MessagingException {

String filePath = Paths.get("src/test/resources/attachment.txt").toAbsolutePath().toString();
String filePath2 = Paths.get("src/test/resources/attachment2.txt").toAbsolutePath().toString();
String filePath3 = Paths.get("src/test/resources/attachment3.txt").toAbsolutePath().toString();

SingleNodeProcessingChainProducer chain = SingleNodeProcessingChainSupport.chainProducer(application, "testMailSink", String.format(
"mail --host=localhost " + "--to='''[email protected]''' --from='''[email protected]''' --replyTo='''[email protected]''' "
+ "--subject='''testXD''' --port=3465 --username='[email protected]' --password='test1' " + "--ssl=true --auth=true --attachmentExpression='''%s;%s;%s''' "
+ "--attachmentFilename='''test.txt;test2.txt;test3.txt'''", filePath, filePath2, filePath3));

chain.sendPayload(filePath);

assertThat(greenMail.waitForIncomingEmail(TIMEOUT, 1), is(true));
chain.destroy();

Message[] messages = greenMail.getReceivedMessages();
assertThat(messages.length, is(1));

assertThat(messages[0].getSubject(), is("testXD"));

MimeMultipart mp = (MimeMultipart)messages[0].getContent();

assertThat(mp.getCount(), is(4));
boolean allPartsArePresents = true;

for(int i=0;i<4;i++) {
try {
if(mp.getBodyPart(i) == null)
allPartsArePresents = false;
} catch (MessagingException e) {
Assert.fail();
}
}

assertThat(allPartsArePresents, is(true));
}

@Test
public void testUnsecuredMailSinkIntegration() throws IOException, InterruptedException, MessagingException {
greenMail.reset();

String filePath = Paths.get("src/test/resources/attachment.txt").toAbsolutePath().toString();
String filePath2 = Paths.get("src/test/resources/attachment2.txt").toAbsolutePath().toString();
String filePath3 = Paths.get("src/test/resources/attachment3.txt").toAbsolutePath().toString();

SingleNodeProcessingChainProducer chain = SingleNodeProcessingChainSupport.chainProducer(application, "testMailSink2", String.format(
"mail --host=localhost " + "--to='''[email protected]''' --from='''[email protected]''' --replyTo='''[email protected]''' "
+ "--subject='''testXD2''' --port=3025 --username='[email protected]' --password='test1' "
+ "--starttls=false --ssl=false --auth=true --attachmentExpression='''%s;%s;%s''' "
+ "--attachmentFilename='''test.txt;test2.txt;test3.txt'''",
filePath, filePath2,filePath3));

chain.sendPayload(filePath);

assertThat(greenMail.waitForIncomingEmail(TIMEOUT, 1), is(true));
chain.destroy();

Message[] messages = greenMail.getReceivedMessages();
assertThat(messages.length, is(1));

assertThat(messages[0].getSubject(), is("testXD2"));

MimeMultipart mp = (MimeMultipart)messages[0].getContent();

assertThat(mp.getCount(), is(4));

boolean allPartsArePresents = true;

for(int i=0;i<4;i++) {
try {
if(mp.getBodyPart(i) == null)
allPartsArePresents = false;
} catch (MessagingException e) {
Assert.fail();
}
}

assertThat(allPartsArePresents, is(true));

}

@AfterClass
public static void tearDown() {
greenMail.stop();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
info.shortDescription = Sends incoming message as email.
options_class = org.springframework.xd.mail.MailSinkOptionsMetadata
4 changes: 4 additions & 0 deletions gradle/build-extensions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ project('spring-xd-extension-mail') {
description = 'Spring XD Mail'
dependencies {
compile "org.springframework.integration:spring-integration-mail"
compile "com.sun.mail:javax.mail"
compile project(":spring-xd-module-spi")
compile project(":spring-xd-dirt")
testCompile project(":spring-xd-test")
testCompile "com.icegreen:greenmail:$greenmailVersion"
}
}

Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Wed May 13 10:44:31 CEST 2015
#Tue Nov 10 21:53:09 CET 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
Expand Down
Loading

0 comments on commit f8b75ce

Please sign in to comment.