Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* This file is part of *** M y C o R e ***
* See http://www.mycore.de/ for details.
*
* MyCoRe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MyCoRe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MyCoRe. If not, see .
*/
package org.mycore.common;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.jdom2.transform.JDOMSource;
import org.mycore.common.MCRMailer.EMail.MessagePart;
import org.mycore.common.config.MCRConfiguration2;
import org.mycore.common.config.MCRConfigurationException;
import org.mycore.common.content.MCRContent;
import org.mycore.common.content.MCRJAXBContent;
import org.mycore.common.content.MCRJDOMContent;
import org.mycore.common.content.transformer.MCRXSL2XMLTransformer;
import org.mycore.common.xsl.MCRParameterCollector;
import org.mycore.frontend.servlets.MCRServlet;
import org.mycore.frontend.servlets.MCRServletJob;
import org.xml.sax.SAXParseException;
/**
* This class provides methods to send emails from within a MyCoRe application.
*
* @author Marc Schluepmann
* @author Frank L\u00FCtzenkirchen
* @author Werner Greßhoff
* @author Ren\u00E9 Adler (eagle)
*
* @version $Revision$ $Date$
*/
public class MCRMailer extends MCRServlet {
private static final Logger LOGGER = LogManager.getLogger(MCRMailer.class);
private static final String DELIMITER = "\n--------------------------------------\n";
private static Session mailSession;
protected static final String ENCODING;
/** How often should MCRMailer try to send mail? */
protected static int numTries;
private static final long serialVersionUID = 1L;
@Override
protected void doGetPost(MCRServletJob job) throws Exception {
String goTo = job.getRequest().getParameter("goto");
String xsl = job.getRequest().getParameter("xsl");
Document input = (Document) (job.getRequest().getAttribute("MCRXEditorSubmission"));
MCRMailer.sendMail(input, xsl);
job.getResponse().sendRedirect(goTo);
}
static {
ENCODING = MCRConfiguration2.getStringOrThrow("MCR.Mail.Encoding");
Properties mailProperties = new Properties();
try {
Authenticator auth = null;
numTries = MCRConfiguration2.getOrThrow("MCR.Mail.NumTries", Integer::parseInt);
if (MCRConfiguration2.getString("MCR.Mail.User").isPresent()
&& MCRConfiguration2.getString("MCR.Mail.Password").isPresent()) {
auth = new SMTPAuthenticator();
mailProperties.setProperty("mail.smtp.auth", "true");
}
mailProperties.setProperty("mail.smtp.host", MCRConfiguration2.getStringOrThrow("MCR.Mail.Server"));
mailProperties.setProperty("mail.transport.protocol",
MCRConfiguration2.getStringOrThrow("MCR.Mail.Protocol"));
mailProperties.setProperty("mail.smtp.port", MCRConfiguration2.getString("MCR.Mail.Port").orElse("25"));
mailSession = Session.getDefaultInstance(mailProperties, auth);
mailSession.setDebug(MCRConfiguration2.getOrThrow("MCR.Mail.Debug", Boolean::parseBoolean));
} catch (MCRConfigurationException mcrx) {
String msg = "Missing e-mail configuration data.";
LOGGER.fatal(msg, mcrx);
}
}
/**
* This method sends a simple plaintext email with the given parameters.
*
* @param sender
* the sender of the email
* @param recipient
* the recipient of the email
* @param subject
* the subject of the email
* @param body
* the textbody of the email
*/
public static void send(String sender, String recipient, String subject, String body) {
LOGGER.debug("Called plaintext send method with single recipient.");
ArrayList recipients = new ArrayList<>();
recipients.add(recipient);
send(sender, null, recipients, null, subject, body, null);
}
/**
* This method sends a simple plaintext email to more than one recipient. If
* flag BCC is true, the sender will also get the email as BCC recipient.
*
* @param sender
* the sender of the email
* @param recipients
* the recipients of the email as a List of Strings
* @param subject
* the subject of the email
* @param body
* the textbody of the email
* @param bcc
* if true, sender will also get a copy as cc recipient
*/
public static void send(String sender, List recipients, String subject, String body, boolean bcc) {
LOGGER.debug("Called plaintext send method with multiple recipients.");
List bccList = null;
if (bcc) {
bccList = new ArrayList<>();
bccList.add(sender);
}
send(sender, null, recipients, bccList, subject, body, null);
}
/**
* This method sends a multipart email with the given parameters.
*
* @param sender
* the sender of the email
* @param recipient
* the recipient of the email
* @param subject
* the subject of the email
* @param parts
* a List of URL strings which should be added as parts
* @param body
* the textbody of the email
*/
public static void send(String sender, String recipient, String subject, String body, List parts) {
LOGGER.debug("Called multipart send method with single recipient.");
ArrayList recipients = new ArrayList<>();
recipients.add(recipient);
send(sender, null, recipients, null, subject, body, parts);
}
/**
* This method sends a multipart email to more than one recipient. If flag
* BCC is true, the sender will also get the email as BCC recipient.
*
* @param sender
* the sender of the email
* @param recipients
* the recipients of the email as a List of Strings
* @param subject
* the subject of the email
* @param body
* the textbody of the email
* @param parts
* a List of URL strings which should be added as parts
* @param bcc
* if true, sender will also get a copy as bcc recipient
*/
public static void send(String sender, List recipients, String subject, String body, List parts,
boolean bcc) {
LOGGER.debug("Called multipart send method with multiple recipients.");
List bccList = null;
if (bcc) {
bccList = new ArrayList<>();
bccList.add(sender);
}
send(sender, null, recipients, bccList, subject, body, parts);
}
/**
* Send email from a given XML document. See the sample mail below:
*
* <email>
* <from>[email protected]</from>
* <to>[email protected]</to>
* <bcc>[email protected]</bcc>
* <subject>Grüße aus der Stadt der Drachen</subject>
* <body>Es ist recht bewölkt. Alles Gute, Jim.</body>
* <body type="html">Es ist recht bewölkt. Alles Gute, Jim.</body>
* <part>http://upload.wikimedia.org/wikipedia/de/f/f7/JimKnopf.jpg</part>
* </email>
*
* @param email the email as JDOM element.
*/
public static void send(Element email) {
try {
send(email, false);
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
}
/**
* Send email from a given XML document. See the sample mail below:
*
* <email>
* <from>[email protected]</from>
* <to>[email protected]</to>
* <bcc>[email protected]</bcc>
* <subject>Grüße aus der Stadt der Drachen</subject>
* <body>Es ist recht bewölkt. Alles Gute, Jim.</body>
* <body type="html">Es ist recht bewölkt. Alles Gute, Jim.</body>
* <part>http://upload.wikimedia.org/wikipedia/de/f/f7/JimKnopf.jpg</part>
* </email>
*
* @param email the email as JDOM element.
* @param allowException allow to throw exceptions if set to true
* @throws Exception
*/
public static void send(Element email, Boolean allowException) throws Exception {
EMail mail = EMail.parseXML(email);
if (allowException) {
if (mail.to == null || mail.to.isEmpty()) {
throw new MCRException("No receiver defined for mail\n" + mail + '\n');
}
trySending(mail);
} else {
send(mail);
}
}
/**
* Sends email. When sending email fails (for example, outgoing mail server
* is not responding), sending will be retried after five minutes. This is
* done up to 10 times.
*
*
* @param from
* the sender of the email
* @param replyTo
* the reply-to addresses as a List of Strings, may be null
* @param to
* the recipients of the email as a List of Strings
* @param bcc
* the bcc recipients of the email as a List of Strings, may be
* null
* @param subject
* the subject of the email
* @param body
* the text of the email
* @param parts
* a List of URL strings which should be added as parts, may be
* null
*/
public static void send(final String from, final List replyTo, final List to,
final List bcc, final String subject, final String body, final List parts) {
EMail mail = new EMail();
mail.from = from;
mail.replyTo = replyTo;
mail.to = to;
mail.bcc = bcc;
mail.subject = subject;
mail.msgParts = new ArrayList<>();
mail.msgParts.add(new MessagePart(body));
mail.parts = parts;
send(mail);
}
/**
* Sends email. When sending email fails (for example, outgoing mail server
* is not responding), sending will be retried after five minutes. This is
* done up to 10 times.
*
* @param mail the email
*/
public static void send(EMail mail) {
if (mail.to == null || mail.to.isEmpty()) {
throw new MCRException("No receiver defined for mail\n" + mail + '\n');
}
try {
if (numTries > 0) {
trySending(mail);
}
} catch (Exception ex) {
LOGGER.info("Sending e-mail failed: ", ex);
if (numTries < 2) {
return;
}
Thread t = new Thread(() -> {
for (int i = numTries - 1; i > 0; i--) {
LOGGER.info("Retrying in 5 minutes...");
try {
Thread.sleep(300000); // wait 5 minutes
} catch (InterruptedException ignored) {
}
try {
trySending(mail);
LOGGER.info("Successfully resended e-mail.");
break;
} catch (Exception ex1) {
LOGGER.info("Sending e-mail failed: ", ex1);
}
}
});
t.start(); // Try to resend mail in separate thread
}
}
private static void trySending(EMail mail) throws Exception {
MimeMessage msg = new MimeMessage(mailSession);
msg.setFrom(EMail.buildAddress(mail.from));
Optional> toList = EMail.buildAddressList(mail.to);
if (toList.isPresent()) {
msg.addRecipients(Message.RecipientType.TO, toList.get().toArray(new InternetAddress[toList.get().size()]));
}
Optional> replyToList = EMail.buildAddressList(mail.replyTo);
if (replyToList.isPresent()) {
msg.setReplyTo((replyToList.get().toArray(new InternetAddress[replyToList.get().size()])));
}
Optional> bccList = EMail.buildAddressList(mail.bcc);
if (bccList.isPresent()) {
msg.addRecipients(Message.RecipientType.BCC,
bccList.get().toArray(new InternetAddress[bccList.get().size()]));
}
msg.setSentDate(new Date());
msg.setSubject(mail.subject, ENCODING);
if (mail.parts != null && !mail.parts.isEmpty() || mail.msgParts != null && mail.msgParts.size() > 1) {
Multipart multipart = new MimeMultipart();
// Create the message part
MimeBodyPart messagePart = new MimeBodyPart();
if (mail.msgParts.size() > 1) {
multipart = new MimeMultipart("mixed");
MimeMultipart alternative = new MimeMultipart("alternative");
for (MessagePart m : mail.msgParts) {
messagePart = new MimeBodyPart();
messagePart.setText(m.message, ENCODING, m.type.value());
alternative.addBodyPart(messagePart);
}
messagePart = new MimeBodyPart();
messagePart.setContent(alternative);
multipart.addBodyPart(messagePart);
} else {
Optional plainMsg = mail.getTextMessage();
if (plainMsg.isPresent()) {
messagePart.setText(plainMsg.get().message, ENCODING);
multipart.addBodyPart(messagePart);
}
}
if (mail.parts != null && !mail.parts.isEmpty()) {
for (String part : mail.parts) {
messagePart = new MimeBodyPart();
URL url = new URL(part);
DataSource source = new URLDataSource(url);
messagePart.setDataHandler(new DataHandler(source));
String fileName = url.getPath();
if (fileName.contains("\\")) {
fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
} else if (fileName.contains("/")) {
fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
}
messagePart.setFileName(fileName);
multipart.addBodyPart(messagePart);
}
}
msg.setContent(multipart);
} else {
Optional plainMsg = mail.getTextMessage();
if (plainMsg.isPresent()) {
msg.setText(plainMsg.get().message, ENCODING);
}
}
LOGGER.info("Sending e-mail to {}", mail.to);
Transport.send(msg);
}
/**
* Generates e-mail from the given input document by transforming it with an xsl stylesheet,
* and sends the e-mail afterwards.
*
* @param input the xml input document
* @param stylesheet the xsl stylesheet that will generate the e-mail, without the ending ".xsl"
* @param parameters the optionally empty table of xsl parameters
* @return the generated e-mail
*
* @see org.mycore.common.MCRMailer
*/
public static Element sendMail(Document input, String stylesheet, Map parameters) throws Exception {
LOGGER.info("Generating e-mail from {} using {}.xsl", input.getRootElement().getName(), stylesheet);
if (LOGGER.isDebugEnabled()) {
debug(input.getRootElement());
}
Element eMail = transform(input, stylesheet, parameters).getRootElement();
if (LOGGER.isDebugEnabled()) {
debug(eMail);
}
if (eMail.getChildren("to").isEmpty()) {
LOGGER.warn("Will not send e-mail, no 'to' address specified");
} else {
LOGGER.info("Sending e-mail to {}: {}", eMail.getChildText("to"), eMail.getChildText("subject"));
MCRMailer.send(eMail);
}
return eMail;
}
/**
* Generates e-mail from the given input document by transforming it with an xsl stylesheet,
* and sends the e-mail afterwards.
*
* @param input the xml input document
* @param stylesheet the xsl stylesheet that will generate the e-mail, without the ending ".xsl"
* @return the generated e-mail
*
* @see org.mycore.common.MCRMailer
*/
public static Element sendMail(Document input, String stylesheet) throws Exception {
return sendMail(input, stylesheet, Collections.emptyMap());
}
/**
* Transforms the given input element using xsl stylesheet.
*
* @param input the input document to transform.
* @param stylesheet the name of the xsl stylesheet to use, without the ".xsl" ending.
* @param parameters the optionally empty table of xsl parameters
* @return the output document generated by the transformation process
*/
private static Document transform(Document input, String stylesheet, Map parameters)
throws Exception {
MCRJDOMContent source = new MCRJDOMContent(input);
MCRXSL2XMLTransformer transformer = MCRXSL2XMLTransformer.getInstance("xsl/" + stylesheet + ".xsl");
MCRParameterCollector parameterCollector = MCRParameterCollector.getInstanceFromUserSession();
parameterCollector.setParameters(parameters);
MCRContent result = transformer.transform(source, parameterCollector);
return result.asXML();
}
/** Outputs xml to the LOGGER for debugging */
private static void debug(Element xml) {
XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
LOGGER.debug(DELIMITER + "{}" + DELIMITER, xout.outputString(xml));
}
@XmlRootElement(name = "email")
public static class EMail {
private static final JAXBContext JAXB_CONTEXT = initContext();
@XmlElement
public String from;
@XmlElement
public List replyTo;
@XmlElement
public List to;
@XmlElement
public List bcc;
@XmlElement
public String subject;
@XmlElement(name = "body")
public List msgParts;
@XmlElement(name = "part")
public List parts;
private static JAXBContext initContext() {
try {
return JAXBContext.newInstance(EMail.class.getPackage().getName(), EMail.class.getClassLoader());
} catch (final JAXBException e) {
throw new MCRException("Could not instantiate JAXBContext.", e);
}
}
/**
* Parse a email from given {@link Element}.
*
* @param xml the email
* @return the {@link EMail} object
*/
public static EMail parseXML(final Element xml) {
try {
final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
return (EMail) unmarshaller.unmarshal(new JDOMSource(xml));
} catch (final JAXBException e) {
throw new MCRException("Exception while transforming Element to EMail.", e);
}
}
/**
* Builds email address from a string. The string may be a single email
* address or a combination of a personal name and address, like "John Doe"
* <[email protected]>
*
* @param s the email address string
* @return a {@link InternetAddress}
* @throws Exception throws AddressException or UnsupportedEncodingException
*/
private static InternetAddress buildAddress(String s) throws Exception {
if (!s.endsWith(">")) {
return new InternetAddress(s.trim());
}
String name = s.substring(0, s.lastIndexOf("<")).trim();
String addr = s.substring(s.lastIndexOf("<") + 1, s.length() - 1).trim();
if (name.startsWith("\"") && name.endsWith("\"")) {
name = name.substring(1, name.length() - 1);
}
return new InternetAddress(addr, name);
}
/**
* Builds a list of email addresses from a string list.
*
* @param addresses the list with email addresses
* @return a list of {@link InternetAddress}s
* @see MCRMailer.EMail#buildAddress(String)
*/
private static Optional> buildAddressList(final List addresses) {
return addresses != null ? Optional.ofNullable(addresses.stream().map(address -> {
try {
return buildAddress(address);
} catch (Exception ex) {
return null;
}
}).collect(Collectors.toList())) : Optional.empty();
}
/**
* Returns the text message part.
*
* @return the text message part
*/
public Optional getTextMessage() {
return msgParts != null ? Optional.ofNullable(msgParts).get().stream()
.filter(m -> m.type.equals(MessageType.TEXT)).findFirst() : Optional.empty();
}
/**
* Returns the HTML message part.
*
* @return the HTML message part
*/
public Optional getHTMLMessage() {
return msgParts != null ? Optional.ofNullable(msgParts).get().stream()
.filter(m -> m.type.equals(MessageType.HTML)).findFirst() : Optional.empty();
}
/**
* Returns the {@link EMail} as XML.
*
* @return the XML
*/
public Document toXML() {
final MCRJAXBContent content = new MCRJAXBContent<>(JAXB_CONTEXT, this);
try {
return content.asXML();
} catch (final SAXParseException | JDOMException | IOException e) {
throw new MCRException("Exception while transforming EMail to JDOM document.", e);
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
final int maxLen = 10;
StringBuilder builder = new StringBuilder();
builder.append("EMail [");
if (from != null) {
builder.append("from=");
builder.append(from);
builder.append(", ");
}
if (replyTo != null) {
builder.append("replyTo=");
builder.append(replyTo.subList(0, Math.min(replyTo.size(), maxLen)));
builder.append(", ");
}
if (to != null) {
builder.append("to=");
builder.append(to.subList(0, Math.min(to.size(), maxLen)));
builder.append(", ");
}
if (bcc != null) {
builder.append("bcc=");
builder.append(bcc.subList(0, Math.min(bcc.size(), maxLen)));
builder.append(", ");
}
if (subject != null) {
builder.append("subject=");
builder.append(subject);
builder.append(", ");
}
if (msgParts != null) {
builder.append("msgParts=");
builder.append(msgParts.subList(0, Math.min(msgParts.size(), maxLen)));
builder.append(", ");
}
if (parts != null) {
builder.append("parts=");
builder.append(parts.subList(0, Math.min(parts.size(), maxLen)));
}
builder.append("]");
return builder.toString();
}
@XmlRootElement(name = "body")
public static class MessagePart {
@XmlAttribute
public MessageType type = MessageType.TEXT;
@XmlValue
public String message;
MessagePart() {
}
public MessagePart(final String message) {
this.message = message;
}
public MessagePart(final String message, final MessageType type) {
this.message = message;
this.type = type;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
final int maxLen = 50;
StringBuilder builder = new StringBuilder();
builder.append("MessagePart [");
if (type != null) {
builder.append("type=");
builder.append(type);
builder.append(", ");
}
if (message != null) {
builder.append("message=");
builder.append(message, 0, Math.min(message.length(), maxLen));
}
builder.append("]");
return builder.toString();
}
}
@XmlType(name = "mcrmailer-messagetype")
@XmlEnum
public enum MessageType {
@XmlEnumValue("text")
TEXT("text"),
@XmlEnumValue("html")
HTML("html");
private final String value;
MessageType(String v) {
value = v;
}
public String value() {
return value;
}
public static MessageType fromValue(String v) {
for (MessageType t : MessageType.values()) {
if (t.value.equals(v)) {
return t;
}
}
throw new IllegalArgumentException(v);
}
}
}
private static class SMTPAuthenticator extends javax.mail.Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(MCRConfiguration2.getStringOrThrow("MCR.Mail.User"),
MCRConfiguration2.getStringOrThrow("MCR.Mail.Password"));
}
}
}