| /* |
| * Copyright (c) 2009, 2018 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2009, 2018 Jason Mehrens. All Rights Reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0, which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| 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 user's temp directory. |
| * |
| * @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 only demo |
| * com.sun.mail.util.logging.MailHandler.pushLevel=WARNING |
| * ## |
| * </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; |
| } |
| |
| /** |
| * No objects are allowed. |
| * @throws IllegalAccessException always. |
| */ |
| private MailHandlerDemo() throws IllegalAccessException { |
| throw new IllegalAccessException(); |
| } |
| } |