/*
* Copyright 2005-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.ws.transport.mail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Header;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.search.HeaderTerm;
import javax.mail.search.SearchTerm;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.transport.AbstractSenderConnection;
import org.springframework.ws.transport.TransportConstants;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.mail.support.MailTransportUtils;
/**
* Implementation of {@link WebServiceConnection} that is used for client-side Mail access. Exposes a {@link Message}
* request and response message.
*
* @author Arjen Poutsma
* @author Greg Turnquist
* @since 1.5.0
*/
public class MailSenderConnection extends AbstractSenderConnection {
private static final Log logger = LogFactory.getLog(MailSenderConnection.class);
private final Session session;
private MimeMessage requestMessage;
private Message responseMessage;
private String requestContentType;
private boolean deleteAfterReceive = false;
private final URLName storeUri;
private final URLName transportUri;
private ByteArrayOutputStream requestBuffer;
private InternetAddress from;
private final InternetAddress to;
private String subject;
private final long receiveTimeout;
private Store store;
private Folder folder;
/** Constructs a new Mail connection with the given parameters. */
protected MailSenderConnection(Session session,
URLName transportUri,
URLName storeUri,
InternetAddress to,
long receiveTimeout) {
Assert.notNull(session, "'session' must not be null");
Assert.notNull(transportUri, "'transportUri' must not be null");
Assert.notNull(storeUri, "'storeUri' must not be null");
Assert.notNull(to, "'to' must not be null");
this.session = session;
this.transportUri = transportUri;
this.storeUri = storeUri;
this.to = to;
this.receiveTimeout = receiveTimeout;
}
/** Returns the request message for this connection. */
public Message getRequestMessage() {
return requestMessage;
}
/** Returns the response message, if any, for this connection. */
public Message getResponseMessage() {
return responseMessage;
}
/*
* Package-friendly setters
*/
void setFrom(InternetAddress from) {
this.from = from;
}
void setSubject(String subject) {
this.subject = subject;
}
/*
* URI
*/
@Override
public URI getUri() throws URISyntaxException {
return MailTransportUtils.toUri(to, subject);
}
/*
* Sending
*/
@Override
protected void onSendBeforeWrite(WebServiceMessage message) throws IOException {
try {
requestMessage = new MimeMessage(session);
requestMessage.setRecipient(Message.RecipientType.TO, to);
requestMessage.setSentDate(new Date());
if (from != null) {
requestMessage.setFrom(from);
}
if (subject != null) {
requestMessage.setSubject(subject);
}
requestBuffer = new ByteArrayOutputStream();
}
catch (MessagingException ex) {
throw new MailTransportException(ex);
}
}
@Override
public void addRequestHeader(String name, String value) throws IOException {
try {
requestMessage.addHeader(name, value);
if (TransportConstants.HEADER_CONTENT_TYPE.equals(name)) {
requestContentType = value;
}
}
catch (MessagingException ex) {
throw new MailTransportException(ex);
}
}
@Override
protected OutputStream getRequestOutputStream() throws IOException {
return requestBuffer;
}
@Override
protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
Transport transport = null;
try {
requestMessage.setDataHandler(
new DataHandler(new ByteArrayDataSource(requestContentType, requestBuffer.toByteArray())));
transport = session.getTransport(transportUri);
transport.connect();
requestMessage.saveChanges();
transport.sendMessage(requestMessage, requestMessage.getAllRecipients());
}
catch (MessagingException ex) {
throw new MailTransportException(ex);
}
finally {
MailTransportUtils.closeService(transport);
}
}
/*
* Receiving
*/
@Override
protected void onReceiveBeforeRead() throws IOException {
try {
String requestMessageId = requestMessage.getMessageID();
Assert.hasLength(requestMessageId, "No Message-ID found on request message [" + requestMessage + "]");
try {
Thread.sleep(receiveTimeout);
}
catch (InterruptedException e) {
// Re-interrupt current thread, to allow other threads to react.
Thread.currentThread().interrupt();
}
openFolder();
SearchTerm searchTerm = new HeaderTerm(MailTransportConstants.HEADER_IN_REPLY_TO, requestMessageId);
Message[] responses = folder.search(searchTerm);
if (responses.length > 0) {
if (responses.length > 1) {
logger.warn("Received more than one response for request with ID [" + requestMessageId + "]");
}
responseMessage = responses[0];
}
if (deleteAfterReceive) {
responseMessage.setFlag(Flags.Flag.DELETED, true);
}
}
catch (MessagingException ex) {
throw new MailTransportException(ex);
}
}
private void openFolder() throws MessagingException {
store = session.getStore(storeUri);
store.connect();
folder = store.getFolder(storeUri);
if (folder == null || !folder.exists()) {
throw new IllegalStateException("No default folder to receive from");
}
if (deleteAfterReceive) {
folder.open(Folder.READ_WRITE);
}
else {
folder.open(Folder.READ_ONLY);
}
}
@Override
protected boolean hasResponse() throws IOException {
return responseMessage != null;
}
@Override
public Iterator<String> getResponseHeaderNames() throws IOException {
try {
List<String> headers = new ArrayList<String>();
Enumeration<?> enumeration = responseMessage.getAllHeaders();
while (enumeration.hasMoreElements()) {
Header header = (Header) enumeration.nextElement();
headers.add(header.getName());
}
return headers.iterator();
}
catch (MessagingException ex) {
throw new MailTransportException(ex);
}
}
@Override
public Iterator<String> getResponseHeaders(String name) throws IOException {
try {
String[] headers = responseMessage.getHeader(name);
return Arrays.asList(headers).iterator();
}
catch (MessagingException ex) {
throw new MailTransportException(ex);
}
}
@Override
protected InputStream getResponseInputStream() throws IOException {
try {
return responseMessage.getDataHandler().getInputStream();
}
catch (MessagingException ex) {
throw new MailTransportException(ex);
}
}
@Override
public boolean hasError() throws IOException {
return false;
}
@Override
public String getErrorMessage() throws IOException {
return null;
}
@Override
public void onClose() throws IOException {
MailTransportUtils.closeFolder(folder, deleteAfterReceive);
MailTransportUtils.closeService(store);
}
private class ByteArrayDataSource implements DataSource {
private byte[] data;
private String contentType;
public ByteArrayDataSource(String contentType, byte[] data) {
this.data = data;
this.contentType = contentType;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public String getContentType() {
return contentType;
}
@Override
public String getName() {
return "ByteArrayDataSource";
}
}
}