/*
 * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
 * Copyright 2009 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 Sun Microsystems 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.MailHandler;
import java.util.logging.*;
import javax.mail.*;
import java.util.Comparator;
import java.io.*;

/**
 * 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 {

    private static final String CLASS_NAME = MailHandlerDemo.class.getName();
    private static final Logger LOGGER = Logger.getLogger(CLASS_NAME);

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        init(); //may create log messages.
        try {
            LOGGER.log(Level.FINEST, "This is the finest part of the demo.",
                    new MessagingException("Fake"));
            LOGGER.log(Level.FINER, "This is the finer part of the demo.",
                    new NullPointerException("Fake"));
            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"));
            LOGGER.log(Level.SEVERE, "The end of the demo.", new IOException("Fake"));
        } finally {
            closeHandlers();
        }
    }

    /**
     * Example for body only messages.
     * On close the remaining messages are sent.
     */
    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.
     */
    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.
     */
    private static void initSimpleAttachment() {
        MailHandler h = new MailHandler();
        h.setSubject("Body and attachment demo");
        h.setAttachmentFormatters(new Formatter[]{new XMLFormatter()});
        h.setAttachmentNames(new String[]{"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.
     */
    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 custom trigger.
     * If the push filter is triggered the message is high priority.
     * Otherwise, on close any remaining messages are sent.
     */
    private static void initWithPushFilter() {
        MailHandler h = new MailHandler();
        h.setSubject("Push on MessagingException demo");
        h.setPushLevel(Level.ALL);
        h.setPushFilter(new MessageErrorsFilter(true));
        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.
     */
    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;
    }
    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.
     */
    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) {

            public void push() {
                super.push();  //push to target.
                super.flush(); //make the target send the email.
            }
        };
        LOGGER.addHandler(m);
        pushNormalHandler = h;
    }
    private static Handler pushNormalHandler;

    /**
     *  Example for various kinds of custom sorting, formatting, and filtering
     *  for multiple attachment messages.
     *  On close any remaining messages are sent.
     */
    private static void initCustomAttachments() {
        MailHandler h = new MailHandler();

        //Sort records by level keeping the severe messages at the top.
        h.setComparator(new LevelAndSeqComparator(true));

        //Use subject to provide a hint as to what is in the email.
        h.setSubject(new SummaryNameFormatter("Log containing {0} records with {1} errors."));

        //Make the body give a simple summary of what happened.
        h.setFormatter(new SummaryFormatter());

        //Create 3 attachments.
        h.setAttachmentFormatters(new Formatter[]{new XMLFormatter(), new XMLFormatter(), new SimpleFormatter()});

        //filter each attachment differently.
        h.setAttachmentFilters(new Filter[]{null, new MessageErrorsFilter(false),
                    new MessageErrorsFilter(true)});


        //create simple names.
        h.setAttachmentNames(new String[]{"all.xml", "errors.xml", "errors.txt"});

        //extract simple name, replace the rest with formatters.
        h.setAttachmentNames(new Formatter[]{h.getAttachmentNames()[0],
                    new SummaryNameFormatter("{0} records and {1} mail errors"),
                    new SummaryNameFormatter("{0,choice,0#no records|1#1 record|" +
                    "1<{0,number,integer} records} and " +
                    "{1,choice,0#no errors|1#1 error|1<" +
                    "{1,number,integer} errors}")});

        LOGGER.addHandler(h);
    }

    /**
     * Sets up the demos that will run.
     */
    private static void init() {
        initBodyOnly();
        initLowCapacity();
        initSimpleAttachment();
        initWithPushLevel();
        initWithPushFilter();
        initCustomAttachments();
        initPushOnly();
        initPushNormal();
        applyFallbackSettings();
    }

    private static void closeHandlers() {
        Handler[] handlers = LOGGER.getHandlers();
        for (int i = 0; i < handlers.length; i++) {
            Handler h = handlers[i];
            h.close();
            LOGGER.removeHandler(h);
        }
    }

    private static void applyFallbackSettings() {
        if (getConfigLocation() == null) {
            LOGGER.setLevel(Level.ALL);
            LOGGER.info("Check your user temp dir for output.");
            Handler[] handlers = LOGGER.getHandlers();
            for (int i = 0; i < handlers.length; i++) {
                Handler h = handlers[i];
                fallbackSettings(h);
            }
            fallbackSettings(pushOnlyHandler);
            fallbackSettings(pushNormalHandler);
        }
    }

    private static void fallbackSettings(Handler h) {
        h.setErrorManager(new FileErrorManager());
        h.setLevel(Level.ALL);
    }

    private static String getTempDir() {
        return System.getProperty("java.io.tmpdir");
    }

    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;
    }

    private static final class MessageErrorsFilter implements Filter {

        private final boolean complement;

        MessageErrorsFilter(boolean complement) {
            this.complement = complement;
        }

        public boolean isLoggable(LogRecord r) {
            return r.getThrown() instanceof MessagingException == complement;
        }
    }

    /**
     * Orders log records by level then sequence number.
     */
    private static final class LevelAndSeqComparator
            implements Comparator, java.io.Serializable {

        private static final long serialVersionUID = 6269562326337300267L;
        private final boolean reverse;

        LevelAndSeqComparator() {
            this(false);
        }

        LevelAndSeqComparator(final boolean reverse) {
            this.reverse = reverse;
        }

        public int compare(Object o1, Object o2) {
            LogRecord r1 = (LogRecord) o1;
            LogRecord r2 = (LogRecord) o2;
            final int first = r1.getLevel().intValue();
            final int second = r2.getLevel().intValue();
            if (first < second) {
                return reverse ? 1 : -1;
            } else if (first > second) {
                return reverse ? -1 : 1;
            } else {
                return compareSeq(r1, r2);
            }
        }

        private int compareSeq(LogRecord r1, LogRecord r2) {
            final long first = r1.getSequenceNumber();
            final long second = r2.getSequenceNumber();
            if (first < second) {
                return reverse ? 1 : -1;
            } else if (first > second) {
                return reverse ? -1 : 1;
            } else {
                return 0;
            }
        }
    }
}
