/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* 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 com.alibaba.citrus.service.mail.builder.content;
import static com.alibaba.citrus.service.mail.MailConstant.*;
import static com.alibaba.citrus.util.Assert.ExceptionType.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.StringUtil.*;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.activation.URLDataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Part;
import com.alibaba.citrus.service.mail.MailNotFoundException;
import com.alibaba.citrus.service.mail.MailService;
import com.alibaba.citrus.service.mail.builder.MailBuilder;
import com.alibaba.citrus.service.mail.builder.MailBuilderException;
import com.alibaba.citrus.service.mail.support.ResourceDataSource;
import com.alibaba.citrus.service.mail.util.MailUtil;
import com.alibaba.citrus.util.StringUtil;
import com.alibaba.citrus.util.ToStringBuilder.MapBuilder;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
/**
* 代表一个邮件的附件。
*
* @author Michael Zhou
*/
public class AttachmentContent extends AbstractContent implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
private AttachmentSource source;
private String fileName;
/** 创建一个附件。 */
public AttachmentContent() {
}
/** 从URL中创建一个附件。 */
public AttachmentContent(URL attachmentURL) {
setURL(attachmentURL);
}
/** 从URL中创建一个指定文件名的附件。 */
public AttachmentContent(URL attachmentURL, String fileName) {
setURL(attachmentURL);
setFileName(fileName);
}
/** 从文件中创建一个附件。 */
public AttachmentContent(File attachmentFile) {
setFile(attachmentFile);
}
/** 从文件中创建一个指定文件名的附件。 */
public AttachmentContent(File attachmentFile, String fileName) {
setFile(attachmentFile);
setFileName(fileName);
}
/** 从<code>DataSource</code>中创建一个附件。 */
public AttachmentContent(DataSource dataSource) {
setDataSource(dataSource);
}
/** 从<code>DataSource</code>中创建一个指定文件名的附件。 */
public AttachmentContent(DataSource dataSource, String fileName) {
setDataSource(dataSource);
setFileName(fileName);
}
/** 从<code>ResourceLoader</code>中装载一个资源并创建附件。 */
public AttachmentContent(String resourceName) {
setResource(resourceName);
}
/** 从<code>ResourceLoader</code>中装载一个资源并创建指定文件名的附件。 */
public AttachmentContent(String resourceName, String fileName) {
setResource(resourceName);
setFileName(fileName);
}
/** 将另一封邮件作为附件。 */
public AttachmentContent(MailBuilder mailBuilder) {
setMail(mailBuilder);
}
/** 将另一封邮件作为附件。 */
public AttachmentContent(Message mail) {
setMail(mail);
}
/** 以URL的内容为附件。 */
public void setURL(URL attachmentURL) {
setSource(new URLSource(this, attachmentURL));
}
/** 以文件的内容为附件。 */
public void setFile(File attachmentFile) {
setSource(new FileSource(this, attachmentFile));
}
/** 以数据源的内容为附件。 */
public void setDataSource(DataSource dataSource) {
setSource(new DsSource(this, dataSource));
}
/** 以resource loader装载的资源内容为附件。 */
public void setResource(String resourceName) {
setSource(new ResourceSource(this, resourceName));
source.containingContent = this;
}
/** 取得用来装载资源的<code>ResourceLoader</code>。 */
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
/** 设置用来装载资源的<code>ResourceLoader</code>。 */
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* 设置附件的文件名。
* <p>
* 该文件名只是用于显示,没有实际的功效。
* </p>
*/
public void setFileName(String fileName) {
this.fileName = trimToNull(fileName);
}
/** 以另一个邮件体为附件。 */
public void setMail(MailBuilder mailBuilder) {
setSource(new MailBuilderSource(this, mailBuilder));
}
/** 以另一个邮件体为附件。 */
public void setMail(Message mail) {
setSource(new MailSource(this, mail));
}
/** 以mail service中取得的另一个邮件体为附件。 */
public void setMail(String attachmentRefId) {
setSource(new MailRefSource(this, attachmentRefId));
}
private void setSource(AttachmentSource source) {
assertNull(this.source, ILLEGAL_STATE, "Attachment source already set: %s", this.source);
this.source = assertNotNull(source, "source");
}
/** 渲染邮件内容。 */
public void render(Part mailPart) throws MessagingException {
assertNotNull(source, "No attachment source was specified");
source.render(mailPart, fileName);
}
/** 创建一个同类型的content。 */
@Override
protected AttachmentContent newInstance() {
return new AttachmentContent();
}
/** 深度复制一个content。 */
@Override
protected void copyTo(AbstractContent copy) {
AttachmentContent copyContent = (AttachmentContent) copy;
if (source != null) {
copyContent.source = source.clone();
copyContent.source.containingContent = copyContent;
}
copyContent.resourceLoader = resourceLoader;
copyContent.fileName = fileName;
}
@Override
protected void toString(MapBuilder mb) {
mb.append("source", source);
mb.append("fileName", fileName);
}
/** 代表一个附件的类型。 */
private abstract static class AttachmentSource implements Cloneable {
protected transient AttachmentContent containingContent;
public AttachmentSource(AttachmentContent containingContent) {
this.containingContent = assertNotNull(containingContent, "containingContent");
}
public final MailBuilder getMailBuilder() {
return containingContent.getMailBuilder();
}
@Override
public final String toString() {
return name() + "[" + desc() + "]";
}
protected abstract String name();
protected abstract String desc();
protected abstract void render(Part mailPart, String fileName);
/** 渲染data source。 */
protected final void render(Part mailPart, String fileName, DataSource source) throws MailBuilderException {
try {
mailPart.setDataHandler(new DataHandler(source));
if (isEmpty(fileName)) {
throw new MailBuilderException("No fileName was specified with " + this);
}
// 确保fileName中不包含/和\
fileName = fileName.replace('\\', '/');
fileName = StringUtil.defaultIfEmpty(StringUtil.substringAfterLast(fileName, "/"), fileName);
mailPart.setFileName(MailUtil.encodeHeader(fileName, getMailBuilder().getCharacterEncoding()));
} catch (MessagingException e) {
throw new MailBuilderException("Failed to add attachment to the mail", e);
} catch (UnsupportedEncodingException e) {
throw new MailBuilderException("Failed to add attachment to the mail", e);
}
}
/** 以另一个邮件作为附件。 */
protected final void render(Part mailPart, Message mail) throws MailBuilderException {
try {
mailPart.setContent(mail, CONTENT_TYPE_MESSAGE);
String subject = mail.getSubject();
if (!StringUtil.isEmpty(subject)) {
mailPart.setDescription(MailUtil.encodeHeader(subject, getMailBuilder().getCharacterEncoding()));
}
} catch (MessagingException e) {
throw new MailBuilderException("Failed to add attachment to the mail", e);
} catch (UnsupportedEncodingException e) {
throw new MailBuilderException("Failed to add attachment to the mail", e);
}
}
@Override
public final AttachmentSource clone() {
try {
return (AttachmentSource) super.clone();
} catch (CloneNotSupportedException e) {
unexpectedException(e);
return null;
}
}
}
private static class URLSource extends AttachmentSource {
private URL url;
public URLSource(AttachmentContent containingContent, URL url) {
super(containingContent);
this.url = assertNotNull(url, "url");
}
@Override
protected String name() {
return "URL";
}
@Override
protected String desc() {
return url.toExternalForm();
}
@Override
protected void render(Part mailPart, String fileName) {
DataSource ds = new URLDataSource(url);
if (fileName == null) {
fileName = url.getPath();
}
render(mailPart, fileName, ds);
}
}
private static class FileSource extends AttachmentSource {
private File file;
public FileSource(AttachmentContent containingContent, File file) {
super(containingContent);
this.file = assertNotNull(file, "file");
}
@Override
protected String name() {
return "File";
}
@Override
protected String desc() {
return file.getAbsolutePath();
}
@Override
protected void render(Part mailPart, String fileName) {
DataSource ds = new FileDataSource(file);
if (fileName == null) {
fileName = file.getAbsolutePath();
}
render(mailPart, fileName, ds);
}
}
private static class DsSource extends AttachmentSource {
private DataSource dataSource;
public DsSource(AttachmentContent containingContent, DataSource dataSource) {
super(containingContent);
this.dataSource = assertNotNull(dataSource, "dataSource");
}
@Override
protected String name() {
return "DataSource";
}
@Override
protected String desc() {
return dataSource.toString();
}
@Override
protected void render(Part mailPart, String fileName) {
if (fileName == null) {
fileName = dataSource.getName();
}
render(mailPart, fileName, dataSource);
}
}
private static class ResourceSource extends AttachmentSource {
private String resourceName;
public ResourceSource(AttachmentContent containingContent, String resourceName) {
super(containingContent);
this.resourceName = assertNotNull(trimToNull(resourceName), "resourceName");
}
@Override
protected String name() {
return "Resource";
}
@Override
protected String desc() {
return resourceName;
}
@Override
protected void render(Part mailPart, String fileName) {
ResourceLoader resourceLoader = containingContent.getResourceLoader();
if (resourceLoader == null) {
throw new MailBuilderException("Could not find resource \"" + resourceName
+ "\": no resourceLoader specified");
}
Resource resource = resourceLoader.getResource(resourceName);
if (!resource.exists()) {
throw new MailBuilderException("Could not find resource \"" + resourceName + "\"");
}
DataSource ds;
try {
ds = new URLDataSource(resource.getURL());
} catch (IOException e) {
ds = new ResourceDataSource(resource);
}
if (fileName == null) {
fileName = resourceName;
}
render(mailPart, fileName, ds);
}
}
private static class MailBuilderSource extends AttachmentSource {
private MailBuilder mailBuilder;
public MailBuilderSource(AttachmentContent containingContent, MailBuilder mailBuilder) {
super(containingContent);
this.mailBuilder = assertNotNull(mailBuilder, "mailBuilder");
}
@Override
protected String name() {
return "MailBuilder";
}
@Override
protected String desc() {
return "id=" + mailBuilder.getId();
}
@Override
protected void render(Part mailPart, String fileName) {
Message mail = mailBuilder.getMessage(getMailBuilder().getSession());
render(mailPart, mail);
}
}
private static class MailSource extends AttachmentSource {
private Message mail;
public MailSource(AttachmentContent containingContent, Message mail) {
super(containingContent);
this.mail = assertNotNull(mail, "mail");
}
@Override
protected String name() {
return "Message";
}
@Override
protected String desc() {
return mail.getClass().getSimpleName();
}
@Override
protected void render(Part mailPart, String fileName) {
render(mailPart, mail);
}
}
private static class MailRefSource extends AttachmentSource {
private String mailRef;
public MailRefSource(AttachmentContent containingContent, String mailRef) {
super(containingContent);
this.mailRef = assertNotNull(trimToNull(mailRef), "mailRef");
}
@Override
protected String name() {
return "MailRef";
}
@Override
protected String desc() {
return mailRef;
}
@Override
protected void render(Part mailPart, String fileName) {
MailService mailService = getMailBuilder().getMailService();
if (mailService == null) {
throw new MailBuilderException("Could not find mail \"" + mailRef + "\": no MailService");
}
MailBuilder mailBuilder;
try {
mailBuilder = mailService.getMailBuilder(mailRef);
} catch (MailNotFoundException e) {
throw new MailBuilderException("Could not find mail \"" + mailRef + "\"", e);
}
Message mail = mailBuilder.getMessage(getMailBuilder().getSession());
render(mailPart, mail);
}
}
}