1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.net;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.util.Date;
23 import java.util.Properties;
24
25 import javax.activation.DataSource;
26 import javax.mail.Authenticator;
27 import javax.mail.Message;
28 import javax.mail.MessagingException;
29 import javax.mail.PasswordAuthentication;
30 import javax.mail.Session;
31 import javax.mail.Transport;
32 import javax.mail.internet.InternetHeaders;
33 import javax.mail.internet.MimeBodyPart;
34 import javax.mail.internet.MimeMessage;
35 import javax.mail.internet.MimeMultipart;
36 import javax.mail.internet.MimeUtility;
37 import javax.mail.util.ByteArrayDataSource;
38
39 import org.apache.logging.log4j.LoggingException;
40 import org.apache.logging.log4j.core.Layout;
41 import org.apache.logging.log4j.core.LogEvent;
42 import org.apache.logging.log4j.core.appender.AbstractManager;
43 import org.apache.logging.log4j.core.appender.ManagerFactory;
44 import org.apache.logging.log4j.core.helpers.CyclicBuffer;
45 import org.apache.logging.log4j.core.helpers.NameUtil;
46 import org.apache.logging.log4j.core.helpers.NetUtils;
47 import org.apache.logging.log4j.util.PropertiesUtil;
48
49
50
51
52 public class SMTPManager extends AbstractManager {
53 private static final SMTPManagerFactory FACTORY = new SMTPManagerFactory();
54
55 private final Session session;
56
57 private final CyclicBuffer<LogEvent> buffer;
58
59 private volatile MimeMessage message;
60
61 private final FactoryData data;
62
63 protected SMTPManager(final String name, final Session session, final MimeMessage message,
64 final FactoryData data) {
65 super(name);
66 this.session = session;
67 this.message = message;
68 this.data = data;
69 this.buffer = new CyclicBuffer<LogEvent>(LogEvent.class, data.numElements);
70 }
71
72 public void add(final LogEvent event) {
73 buffer.add(event);
74 }
75
76 public static SMTPManager getSMTPManager(final String to, final String cc, final String bcc,
77 final String from, final String replyTo,
78 final String subject, String protocol, final String host,
79 final int port, final String username, final String password,
80 final boolean isDebug, final String filterName, final int numElements) {
81 if (protocol == null || protocol.length() == 0) {
82 protocol = "smtp";
83 }
84
85 final StringBuilder sb = new StringBuilder();
86 if (to != null) {
87 sb.append(to);
88 }
89 sb.append(":");
90 if (cc != null) {
91 sb.append(cc);
92 }
93 sb.append(":");
94 if (bcc != null) {
95 sb.append(bcc);
96 }
97 sb.append(":");
98 if (from != null) {
99 sb.append(from);
100 }
101 sb.append(":");
102 if (replyTo != null) {
103 sb.append(replyTo);
104 }
105 sb.append(":");
106 if (subject != null) {
107 sb.append(subject);
108 }
109 sb.append(":");
110 sb.append(protocol).append(":").append(host).append(":").append("port").append(":");
111 if (username != null) {
112 sb.append(username);
113 }
114 sb.append(":");
115 if (password != null) {
116 sb.append(password);
117 }
118 sb.append(isDebug ? ":debug:" : "::");
119 sb.append(filterName);
120
121 final String name = "SMTP:" + NameUtil.md5(sb.toString());
122
123 return getManager(name, FACTORY, new FactoryData(to, cc, bcc, from, replyTo, subject,
124 protocol, host, port, username, password, isDebug, numElements));
125 }
126
127
128
129
130
131
132 public void sendEvents(final Layout<?> layout, final LogEvent appendEvent) {
133 if (message == null) {
134 connect();
135 }
136 try {
137 final LogEvent[] priorEvents = buffer.removeAll();
138 final byte[] rawBytes = formatContentToBytes(priorEvents, appendEvent, layout);
139
140 final String contentType = layout.getContentType();
141 final String encoding = getEncoding(rawBytes, contentType);
142 final byte[] encodedBytes = encodeContentToBytes(rawBytes, encoding);
143
144 final InternetHeaders headers = getHeaders(contentType, encoding);
145 final MimeMultipart mp = getMimeMultipart(encodedBytes, headers);
146
147 sendMultipartMessage(message, mp);
148 } catch (final MessagingException e) {
149 LOGGER.error("Error occurred while sending e-mail notification.", e);
150 throw new LoggingException("Error occurred while sending email", e);
151 } catch (final IOException e) {
152 LOGGER.error("Error occurred while sending e-mail notification.", e);
153 throw new LoggingException("Error occurred while sending email", e);
154 } catch (final RuntimeException e) {
155 LOGGER.error("Error occurred while sending e-mail notification.", e);
156 throw new LoggingException("Error occurred while sending email", e);
157 }
158 }
159
160 protected byte[] formatContentToBytes(final LogEvent[] priorEvents, final LogEvent appendEvent,
161 final Layout<?> layout) throws IOException {
162 final ByteArrayOutputStream raw = new ByteArrayOutputStream();
163 writeContent(priorEvents, appendEvent, layout, raw);
164 return raw.toByteArray();
165 }
166
167 private void writeContent(final LogEvent[] priorEvents, final LogEvent appendEvent, final Layout<?> layout,
168 final ByteArrayOutputStream out)
169 throws IOException {
170 writeHeader(layout, out);
171 writeBuffer(priorEvents, appendEvent, layout, out);
172 writeFooter(layout, out);
173 }
174
175 protected void writeHeader(final Layout<?> layout, final OutputStream out) throws IOException {
176 final byte[] header = layout.getHeader();
177 if (header != null) {
178 out.write(header);
179 }
180 }
181
182 protected void writeBuffer(final LogEvent[] priorEvents, final LogEvent appendEvent, final Layout<?> layout,
183 final OutputStream out) throws IOException {
184 for (final LogEvent priorEvent : priorEvents) {
185 final byte[] bytes = layout.toByteArray(priorEvent);
186 out.write(bytes);
187 }
188
189 final byte[] bytes = layout.toByteArray(appendEvent);
190 out.write(bytes);
191 }
192
193 protected void writeFooter(final Layout<?> layout, final OutputStream out) throws IOException {
194 final byte[] footer = layout.getFooter();
195 if (footer != null) {
196 out.write(footer);
197 }
198 }
199
200 protected String getEncoding(final byte[] rawBytes, final String contentType) {
201 final DataSource dataSource = new ByteArrayDataSource(rawBytes, contentType);
202 return MimeUtility.getEncoding(dataSource);
203 }
204
205 protected byte[] encodeContentToBytes(final byte[] rawBytes, final String encoding)
206 throws MessagingException, IOException {
207 final ByteArrayOutputStream encoded = new ByteArrayOutputStream();
208 encodeContent(rawBytes, encoding, encoded);
209 return encoded.toByteArray();
210 }
211
212 protected void encodeContent(final byte[] bytes, final String encoding, final ByteArrayOutputStream out)
213 throws MessagingException, IOException {
214 final OutputStream encoder = MimeUtility.encode(out, encoding);
215 encoder.write(bytes);
216 encoder.close();
217 }
218
219 protected InternetHeaders getHeaders(final String contentType, final String encoding) {
220 final InternetHeaders headers = new InternetHeaders();
221 headers.setHeader("Content-Type", contentType + "; charset=UTF-8");
222 headers.setHeader("Content-Transfer-Encoding", encoding);
223 return headers;
224 }
225
226 protected MimeMultipart getMimeMultipart(final byte[] encodedBytes, final InternetHeaders headers)
227 throws MessagingException {
228 final MimeMultipart mp = new MimeMultipart();
229 final MimeBodyPart part = new MimeBodyPart(headers, encodedBytes);
230 mp.addBodyPart(part);
231 return mp;
232 }
233
234 protected void sendMultipartMessage(final MimeMessage message, final MimeMultipart mp) throws MessagingException {
235 synchronized (message) {
236 message.setContent(mp);
237 message.setSentDate(new Date());
238 Transport.send(message);
239 }
240 }
241
242
243
244
245 private static class FactoryData {
246 private final String to;
247 private final String cc;
248 private final String bcc;
249 private final String from;
250 private final String replyto;
251 private final String subject;
252 private final String protocol;
253 private final String host;
254 private final int port;
255 private final String username;
256 private final String password;
257 private final boolean isDebug;
258 private final int numElements;
259
260 public FactoryData(final String to, final String cc, final String bcc, final String from, final String replyTo,
261 final String subject, final String protocol, final String host, final int port,
262 final String username, final String password, final boolean isDebug, final int numElements) {
263 this.to = to;
264 this.cc = cc;
265 this.bcc = bcc;
266 this.from = from;
267 this.replyto = replyTo;
268 this.subject = subject;
269 this.protocol = protocol;
270 this.host = host;
271 this.port = port;
272 this.username = username;
273 this.password = password;
274 this.isDebug = isDebug;
275 this.numElements = numElements;
276 }
277 }
278
279 private synchronized void connect() {
280 if (message != null) {
281 return;
282 }
283 try {
284 message = new MimeMessageBuilder(session).setFrom(data.from).setReplyTo(data.replyto)
285 .setRecipients(Message.RecipientType.TO, data.to).setRecipients(Message.RecipientType.CC, data.cc)
286 .setRecipients(Message.RecipientType.BCC, data.bcc).setSubject(data.subject).getMimeMessage();
287 } catch (final MessagingException e) {
288 LOGGER.error("Could not set SMTPAppender message options.", e);
289 message = null;
290 }
291 }
292
293
294
295
296 private static class SMTPManagerFactory implements ManagerFactory<SMTPManager, FactoryData> {
297
298 public SMTPManager createManager(final String name, final FactoryData data) {
299 final String prefix = "mail." + data.protocol;
300
301 final Properties properties = PropertiesUtil.getSystemProperties();
302 properties.put("mail.transport.protocol", data.protocol);
303 if (properties.getProperty("mail.host") == null) {
304
305 properties.put("mail.host", NetUtils.getLocalHostname());
306 }
307
308 if (null != data.host) {
309 properties.put(prefix + ".host", data.host);
310 }
311 if (data.port > 0) {
312 properties.put(prefix + ".port", String.valueOf(data.port));
313 }
314
315 final Authenticator authenticator = buildAuthenticator(data.username, data.password);
316 if (null != authenticator) {
317 properties.put(prefix + ".auth", "true");
318 }
319
320 final Session session = Session.getInstance(properties, authenticator);
321 session.setProtocolForAddress("rfc822", data.protocol);
322 session.setDebug(data.isDebug);
323 MimeMessage message;
324
325 try {
326 message = new MimeMessageBuilder(session).setFrom(data.from).setReplyTo(data.replyto)
327 .setRecipients(Message.RecipientType.TO, data.to).setRecipients(Message.RecipientType.CC, data.cc)
328 .setRecipients(Message.RecipientType.BCC, data.bcc).setSubject(data.subject).getMimeMessage();
329 } catch (final MessagingException e) {
330 LOGGER.error("Could not set SMTPAppender message options.", e);
331 message = null;
332 }
333
334 return new SMTPManager(name, session, message, data);
335 }
336
337 private Authenticator buildAuthenticator(final String username, final String password) {
338 if (null != password && null != username) {
339 return new Authenticator() {
340 private final PasswordAuthentication passwordAuthentication =
341 new PasswordAuthentication(username, password);
342
343 @Override
344 protected PasswordAuthentication getPasswordAuthentication() {
345 return passwordAuthentication;
346 }
347 };
348 }
349 return null;
350 }
351 }
352 }