MailHandlerDemo.java revision 823
/*
* Copyright (c) 2009-2016 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009-2016 Jason Mehrens. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import com.sun.mail.util.logging.CollectorFormatter;
import com.sun.mail.util.logging.DurationFilter;
import com.sun.mail.util.logging.MailHandler;
import com.sun.mail.util.logging.SeverityComparator;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.util.*;
import java.util.logging.*;
import javax.activation.DataHandler;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
/**
* Demo for the different configurations for the MailHandler. If the logging
* properties file or class is not specified then this demo will apply some
* default settings to store emails in the users temp dir.
*
* @author Jason Mehrens
*/
public class MailHandlerDemo {
/**
* This class name.
*/
private static final String CLASS_NAME = MailHandlerDemo.class.getName();
/**
* The logger for this class name.
*/
private static final Logger LOGGER = Logger.getLogger(CLASS_NAME);
/**
* Runs the demo.
*
* @param args the command line arguments
* @throws IOException if there is a problem.
*/
public static void main(String[] args) throws IOException {
List<String> l = Arrays.asList(args);
if (l.contains("/?") || l.contains("-?") || l.contains("-help")) {
LOGGER.info("Usage: java MailHandlerDemo "
+ "[[-all] | [-body] | [-custom] | [-debug] | [-low] "
+ "| [-simple] | [-pushlevel] | [-pushfilter] "
+ "| [-pushnormal] | [-pushonly]] "
+ "\n\n"
+ "-all\t\t: Execute all demos.\n"
+ "-body\t\t: An email with all records and only a body.\n"
+ "-custom\t\t: An email with attachments and dynamic names.\n"
+ "-debug\t\t: Output basic debug information about the JVM "
+ "and log configuration.\n"
+ "-low\t\t: Generates multiple emails due to low capacity."
+ "\n"
+ "-simple\t\t: An email with all records with body and "
+ "an attachment.\n"
+ "-pushlevel\t: Generates high priority emails when the"
+ " push level is triggered and normal priority when "
+ "flushed.\n"
+ "-pushFilter\t: Generates high priority emails when the "
+ "push level and the push filter is triggered and normal "
+ "priority emails when flushed.\n"
+ "-pushnormal\t: Generates multiple emails when the "
+ "MemoryHandler push level is triggered. All generated "
+ "email are sent as normal priority.\n"
+ "-pushonly\t: Generates multiple emails when the "
+ "MemoryHandler push level is triggered. Generates high "
+ "priority emails when the push level is triggered and "
+ "normal priority when flushed.\n");
} else {
final boolean debug = init(l); //may create log messages.
try {
LOGGER.log(Level.FINEST, "This is the finest part of the demo.",
new MessagingException("Fake JavaMail issue."));
LOGGER.log(Level.FINER, "This is the finer part of the demo.",
new NullPointerException("Fake bug."));
LOGGER.log(Level.FINE, "This is the fine part of the demo.");
LOGGER.log(Level.CONFIG, "Logging config file is {0}.",
getConfigLocation());
LOGGER.log(Level.INFO, "Your temp directory is {0}, "
+ "please wait...", getTempDir());
try { //Waste some time for the custom formatter.
Thread.sleep(3L * 1000L);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
LOGGER.log(Level.WARNING, "This is a warning.",
new FileNotFoundException("Fake file chooser issue."));
LOGGER.log(Level.SEVERE, "The end of the demo.",
new IOException("Fake access denied issue."));
} finally {
closeHandlers();
}
//Force parse errors. This does have side effects.
if (debug && getConfigLocation() != null) {
LogManager.getLogManager().readConfiguration();
}
}
}
/**
* Used debug problems with the logging.properties. The system property
* java.security.debug=access,stack can be used to trace access to the
* LogManager reset.
*
* @param prefix a string to prefix the output.
* @param err any PrintStream or null for System.out.
*/
@SuppressWarnings("UseOfSystemOutOrSystemErr")
private static void checkConfig(String prefix, PrintStream err) {
if (prefix == null || prefix.trim().length() == 0) {
prefix = "DEBUG";
}
if (err == null) {
err = System.out;
}
try {
err.println(prefix + ": java.version="
+ System.getProperty("java.version"));
err.println(prefix + ": LOGGER=" + LOGGER.getLevel());
err.println(prefix + ": JVM id "
+ ManagementFactory.getRuntimeMXBean().getName());
err.println(prefix + ": java.security.debug="
+ System.getProperty("java.security.debug"));
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
err.println(prefix + ": SecurityManager.class="
+ sm.getClass().getName());
err.println(prefix + ": SecurityManager classLoader="
+ toString(sm.getClass().getClassLoader()));
err.println(prefix + ": SecurityManager.toString=" + sm);
} else {
err.println(prefix + ": SecurityManager.class=null");
err.println(prefix + ": SecurityManager.toString=null");
err.println(prefix + ": SecurityManager classLoader=null");
}
String policy = System.getProperty("java.security.policy");
if (policy != null) {
File f = new File(policy);
err.println(prefix + ": AbsolutePath=" + f.getAbsolutePath());
err.println(prefix + ": CanonicalPath=" + f.getCanonicalPath());
err.println(prefix + ": length=" + f.length());
err.println(prefix + ": canRead=" + f.canRead());
err.println(prefix + ": lastModified="
+ new java.util.Date(f.lastModified()));
}
LogManager manager = LogManager.getLogManager();
String key = "java.util.logging.config.file";
String cfg = System.getProperty(key);
if (cfg != null) {
err.println(prefix + ": " + cfg);
File f = new File(cfg);
err.println(prefix + ": AbsolutePath=" + f.getAbsolutePath());
err.println(prefix + ": CanonicalPath=" + f.getCanonicalPath());
err.println(prefix + ": length=" + f.length());
err.println(prefix + ": canRead=" + f.canRead());
err.println(prefix + ": lastModified="
+ new java.util.Date(f.lastModified()));
} else {
err.println(prefix + ": " + key
+ " is not set as a system property.");
}
err.println(prefix + ": LogManager.class="
+ manager.getClass().getName());
err.println(prefix + ": LogManager classLoader="
+ toString(manager.getClass().getClassLoader()));
err.println(prefix + ": LogManager.toString=" + manager);
err.println(prefix + ": MailHandler classLoader="
+ toString(MailHandler.class.getClassLoader()));
err.println(prefix + ": Context ClassLoader="
+ toString(Thread.currentThread().getContextClassLoader()));
err.println(prefix + ": Session ClassLoader="
+ toString(Session.class.getClassLoader()));
err.println(prefix + ": DataHandler ClassLoader="
+ toString(DataHandler.class.getClassLoader()));
final String p = MailHandler.class.getName();
key = p.concat(".mail.to");
String to = manager.getProperty(key);
err.println(prefix + ": TO=" + to);
if (to != null) {
err.println(prefix + ": TO="
+ Arrays.toString(InternetAddress.parse(to, true)));
}
key = p.concat(".mail.from");
String from = manager.getProperty(key);
if (from == null || from.length() == 0) {
Session session = Session.getInstance(new Properties());
InternetAddress local = InternetAddress.getLocalAddress(session);
err.println(prefix + ": FROM=" + local);
} else {
err.println(prefix + ": FROM="
+ Arrays.asList(InternetAddress.parse(from, false)));
err.println(prefix + ": FROM="
+ Arrays.asList(InternetAddress.parse(from, true)));
}
synchronized (manager) {
final Enumeration<String> e = manager.getLoggerNames();
while (e.hasMoreElements()) {
final Logger l = manager.getLogger(e.nextElement());
if (l != null) {
final Handler[] handlers = l.getHandlers();
if (handlers.length > 0) {
err.println(prefix + ": " + l.getClass().getName()
+ ", " + l.getName());
for (Handler h : handlers) {
err.println(prefix + ":\t" + toString(prefix, err, h));
}
}
}
}
}
} catch (Throwable error) {
err.print(prefix + ": ");
error.printStackTrace(err);
}
err.flush();
}
/**
* Gets the class loader list.
*
* @param cl the class loader or null.
* @return the class loader list.
*/
private static String toString(ClassLoader cl) {
StringBuilder buf = new StringBuilder();
buf.append(cl);
while (cl != null) {
cl = cl.getParent();
buf.append("<-").append(cl);
}
return buf.toString();
}
/**
* Gets a formatting string describing the given handler.
*
* @param prefix the output prefix.
* @param err the error stream.
* @param h the handler.
* @return the formatted string.
*/
private static String toString(String prefix, PrintStream err, Handler h) {
StringBuilder buf = new StringBuilder();
buf.append(h.getClass().getName());
try {
if (h instanceof MailHandler) {
MailHandler mh = (MailHandler) h;
buf.append(", ").append(mh.getSubject());
}
} catch (SecurityException error) {
err.print(prefix + ": ");
error.printStackTrace(err);
}
try {
buf.append(", ").append(h.getFormatter());
} catch (SecurityException error) {
err.print(prefix + ": ");
error.printStackTrace(err);
}
try {
if (h instanceof MailHandler) {
MailHandler mh = (MailHandler) h;
buf.append(", ").append(Arrays.toString(
mh.getAttachmentFormatters()));
}
} catch (SecurityException error) {
err.print(prefix + ": ");
error.printStackTrace(err);
}
try {
buf.append(", ").append(h.getLevel());
} catch (SecurityException error) {
err.print(prefix + ": ");
error.printStackTrace(err);
}
try {
buf.append(", ").append(h.getFilter());
} catch (SecurityException error) {
err.print(prefix + ": ");
error.printStackTrace(err);
}
try {
buf.append(", ").append(h.getErrorManager());
} catch (SecurityException error) {
err.print(prefix + ": ");
error.printStackTrace(err);
}
buf.append(", ").append(toString(h.getClass().getClassLoader()));
return buf.toString();
}
/**
* Example for body only messages. On close the remaining messages are sent. <code>
* ##logging.properties
* MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
* com.sun.mail.util.logging.MailHandler.subject=Body only demo
* ##
* </code>
*/
private static void initBodyOnly() {
MailHandler h = new MailHandler();
h.setSubject("Body only demo");
LOGGER.addHandler(h);
}
/**
* Example showing that when the mail handler reaches capacity it will
* format and send the current records. Capacity is used to roughly limit
* the size of an outgoing message. On close any remaining messages are
* sent. <code>
* ##logging.properties
* MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
* com.sun.mail.util.logging.MailHandler.subject=Low capacity demo
* com.sun.mail.util.logging.MailHandler.capacity=5
* ##
* </code>
*/
private static void initLowCapacity() {
MailHandler h = new MailHandler(5);
h.setSubject("Low capacity demo");
LOGGER.addHandler(h);
}
/**
* Example for body only messages. On close any remaining messages are sent. <code>
* ##logging.properties
* MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
* com.sun.mail.util.logging.MailHandler.subject=Body and attachment demo
* com.sun.mail.util.logging.MailHandler.attachment.formatters=java.util.logging.XMLFormatter
* com.sun.mail.util.logging.MailHandler.attachment.names=data.xml
* ##
* </code>
*/
private static void initSimpleAttachment() {
MailHandler h = new MailHandler();
h.setSubject("Body and attachment demo");
h.setAttachmentFormatters(new XMLFormatter());
h.setAttachmentNames("data.xml");
LOGGER.addHandler(h);
}
/**
* Example setup for priority messages by level. If the push level is
* triggered the message is high priority. Otherwise, on close any remaining
* messages are sent. <code>
* ##logging.properties
* MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
* com.sun.mail.util.logging.MailHandler.subject=Push level demo
* com.sun.mail.util.logging.MailHandler.pushLevel=WARNING
* ##
* </code>
*/
private static void initWithPushLevel() {
MailHandler h = new MailHandler();
h.setSubject("Push level demo");
h.setPushLevel(Level.WARNING);
LOGGER.addHandler(h);
}
/**
* Example for priority messages by generation rate. If the push filter is
* triggered the message is high priority. Otherwise, on close any remaining
* messages are sent. If the capacity is set to the <code>
* ##logging.properties
* MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
* com.sun.mail.util.logging.MailHandler.subject=Push filter demo
* com.sun.mail.util.logging.MailHandler.pushLevel=ALL
* com.sun.mail.util.logging.MailHandler.pushFilter=com.sun.mail.util.logging.DurationFilter
* com.sun.mail.util.logging.DurationFilter.records=2
* com.sun.mail.util.logging.DurationFilter.duration=1 * 60 * 1000
* ##
* </code>
*/
private static void initWithPushFilter() {
MailHandler h = new MailHandler();
h.setSubject("Push filter demo");
h.setPushLevel(Level.ALL);
h.setPushFilter(new DurationFilter(2, 1L * 60L * 1000L));
LOGGER.addHandler(h);
}
/**
* Example for circular buffer behavior. The level, push level, and capacity
* are set the same so that the memory handler push results in a mail
* handler push. All messages are high priority. On close any remaining
* records are discarded because they never reach the mail handler. <code>
* ##logging.properties
* MailHandlerDemo.handlers=java.util.logging.MemoryHandler
* java.util.logging.MemoryHandler.target=com.sun.mail.util.logging.MailHandler
* com.sun.mail.util.logging.MailHandler.level=ALL
* java.util.logging.MemoryHandler.level=ALL
* java.util.logging.MemoryHandler.push=WARNING
* com.sun.mail.util.logging.MailHandler.subject=Push on MessagingException demo
* com.sun.mail.util.logging.MailHandler.pushLevel=ALL
* com.sun.mail.util.logging.MailHandler.pushFilter=MailHandlerDemo$MessageErrorsFilter
* ##
* </code>
*/
private static void initPushOnly() {
final int capacity = 3;
final Level pushLevel = Level.WARNING;
final MailHandler h = new MailHandler(capacity);
h.setPushLevel(pushLevel);
h.setSubject("Push only demo");
MemoryHandler m = new MemoryHandler(h, capacity, pushLevel);
h.setLevel(m.getLevel());
LOGGER.addHandler(m);
pushOnlyHandler = h;
}
/**
* Holds on to the push only handler. Only declared here to apply fallback
* settings.
*/
private static Handler pushOnlyHandler;
/**
* Example for circular buffer behavior as normal priority. The push level,
* and capacity are set the same so that the memory handler push results in
* a mail handler push. All messages are normal priority. On close any
* remaining records are discarded because they never reach the mail
* handler. Use the LogManager config option or extend the MemoryHandler to
* emulate this behavior via the logging.properties.
*/
private static void initPushNormal() {
final int capacity = 3;
final MailHandler h = new MailHandler(capacity);
h.setSubject("Push normal demo");
MemoryHandler m = new MemoryHandler(h, capacity, Level.WARNING) {
@Override
public void push() {
super.push(); //push to target.
super.flush(); //make the target send the email.
}
};
LOGGER.addHandler(m);
pushNormalHandler = h;
}
/**
* Holds on to the push normal handler. Only declared here to apply fallback
* settings.
*/
private static Handler pushNormalHandler;
/**
* Example for various kinds of custom sorting, formatting, and filtering
* for multiple attachment messages. The subject will contain the most
* severe record and a count of remaining records. The log records are
* ordered from most severe to least severe. The body uses a custom
* formatter that includes a summary by date and time. The attachment use
* XML and plain text formats. Each attachment has a different set of
* filtering. The attachment names are generated from either a fixed name or
* are built using the number and type of the records formatted. On close
* any remaining messages are sent. Use the LogManager config option or
* extend the MemoryHandler to emulate this behavior via the
* logging.properties.
*/
private static void initCustomAttachments() {
MailHandler h = new MailHandler();
//Sort records by severity keeping the severe messages at the top.
h.setComparator(Collections.reverseOrder(new SeverityComparator()));
//Use subject to provide a hint as to what is in the email.
h.setSubject(new CollectorFormatter());
//Make the body give a simple summary of what happened.
h.setFormatter(new SummaryFormatter());
//Create 3 attachments.
h.setAttachmentFormatters(new XMLFormatter(),
new XMLFormatter(), new SimpleFormatter());
//Filter each attachment differently.
h.setAttachmentFilters(null,
new DurationFilter(3L, 1000L),
new DurationFilter(1L, 15L * 60L * 1000L));
//Creating the attachment name formatters.
h.setAttachmentNames(new CollectorFormatter("all.xml"),
new CollectorFormatter("{3} records and {5} errors.xml"),
new CollectorFormatter("{5,choice,0#no errors|1#1 error|1<"
+ "{5,number,integer} errors}.txt"));
LOGGER.addHandler(h);
}
/**
* Sets up the demos that will run.
*
* @param l the list of arguments.
* @return true if debug is on.
*/
private static boolean init(List<String> l) {
l = new ArrayList<String>(l);
Session session = Session.getInstance(System.getProperties());
boolean all = l.remove("-all") || l.isEmpty();
if (l.remove("-body") || all) {
initBodyOnly();
}
if (l.remove("-custom") || all) {
initCustomAttachments();
}
if (l.remove("-low") || all) {
initLowCapacity();
}
if (l.remove("-pushfilter") || all) {
initWithPushFilter();
}
if (l.remove("-pushlevel") || all) {
initWithPushLevel();
}
if (l.remove("-pushnormal") || all) {
initPushNormal();
}
if (l.remove("-pushonly") || all) {
initPushOnly();
}
if (l.remove("-simple") || all) {
initSimpleAttachment();
}
boolean fallback = applyFallbackSettings();
boolean debug = l.remove("-debug") || session.getDebug();
if (debug) {
checkConfig(CLASS_NAME, session.getDebugOut());
}
if (!l.isEmpty()) {
LOGGER.log(Level.SEVERE, "Unknown commands: {0}", l);
}
if (fallback) {
LOGGER.info("Check your user temp dir for output.");
}
return debug;
}
/**
* Close and remove all handlers added to the class logger.
*/
private static void closeHandlers() {
Handler[] handlers = LOGGER.getHandlers();
for (Handler h : handlers) {
h.close();
LOGGER.removeHandler(h);
}
}
/**
* Apply some fallback settings if no configuration file was specified.
*
* @return true if fallback settings were applied.
*/
private static boolean applyFallbackSettings() {
if (getConfigLocation() == null) {
LOGGER.setLevel(Level.ALL);
Handler[] handlers = LOGGER.getHandlers();
for (Handler h : handlers) {
fallbackSettings(h);
}
fallbackSettings(pushOnlyHandler);
fallbackSettings(pushNormalHandler);
return true;
}
return false;
}
/**
* Common fallback settings for a single handler.
*
* @param h the handler.
*/
private static void fallbackSettings(Handler h) {
if (h != null) {
h.setErrorManager(new FileErrorManager());
h.setLevel(Level.ALL);
}
}
/**
* Gets the system temp directory.
*
* @return the system temp directory.
*/
private static String getTempDir() {
return System.getProperty("java.io.tmpdir");
}
/**
* Gets the configuration file or class name.
*
* @return the file name or class name.
*/
private static String getConfigLocation() {
String file = System.getProperty("java.util.logging.config.file");
if (file == null) {
return System.getProperty("java.util.logging.config.class");
}
return file;
}
}