/* * Copyright (c) 2009-2014 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2009-2014 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 java.io.*; import java.lang.reflect.Constructor; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.logging.ErrorManager; import java.util.logging.Level; import java.util.logging.LogManager; /** * An error manager used to store mime messages from the MailHandler * to the file system when the email server is unavailable or unreachable. The * code to manually setup this error manager can be as simple as the following: *
 *      File dir = new File("path to dir");
 *      FileErrorManager em = new FileErrorManager(dir);
 * 
* *

* Configuration: * The code to setup this error manager via the logging properties can be as * simple as the following: *

 *      #Default FileErrorManager settings.
 *      FileErrorManager.pattern = path to directory
 * 
* * If properties are not defined, or contain invalid values, then the specified * default values are used. * * * @author Jason Mehrens */ public class FileErrorManager extends ErrorManager { /** * Stores the LogManager. */ private static final LogManager manager = LogManager.getLogManager(); /** * Used to report errors that this error manager fails to report. */ private final ErrorManager next = new ErrorManager(); /** * Directory of the email store. */ private final File emailStore; /** * Creates a new error manager. Files are stored in the users temp * directory. * * @exception SecurityException if unable to access system properties or if * a security manager is present and unable to read or write to users temp * directory. */ public FileErrorManager() { this.emailStore = getEmailStore(); init(); } /** * Creates a new error manager. * * @param dir a directory to store the email files. * @throws NullPointerException if dir is null * @throws IllegalArgumentException if dir is a * java.io.File subclass, not a directory, or is not an absolute * path. * @throws SecurityException if a security manager is present and unable to * read or write to a given directory. */ public FileErrorManager(File dir) { this.emailStore = dir; init(); } /** * If the message parameter is a raw email, and passes the store term, then * this method will store the email to the file system. If the message * parameter is not a raw email then the message is forwarded to the super * class. If an email is written to the file system without error, then the * original reported error is ignored. * * @param msg String raw email or plain error message. * @param ex Exception that occurred in the mail handler. * @param code int error manager code. */ @Override public void error(String msg, Exception ex, int code) { if (isRawEmail(msg)) { try { storeEmail(msg); } catch (final IOException IOE) { next.error(msg, ex, code); super.error(emailStore.toString(), IOE, ErrorManager.GENERIC_FAILURE); } catch (final RuntimeException RE) { next.error(msg, ex, code); super.error(emailStore.toString(), RE, ErrorManager.GENERIC_FAILURE); } } else { next.error(msg, ex, code); } } /** * Performs the initialization for this object. */ private void init() { if (next == null) { throw new NullPointerException(ErrorManager.class.getName()); } File dir = this.emailStore; if (dir.getClass() != File.class) { //For security reasons. throw new IllegalArgumentException(dir.getClass().getName()); } if (!dir.isDirectory()) { throw new IllegalArgumentException("File must be a directory."); } if (!dir.canWrite()) { //Can throw under a security manager. super.error(dir.getAbsolutePath(), new SecurityException("write"), ErrorManager.OPEN_FAILURE); } //For now, only absolute paths are allowed. if (!dir.isAbsolute()) { throw new IllegalArgumentException("Only absolute paths are allowed."); } if (!dir.canRead()) { //Can throw under a security manager. super.error(dir.getAbsolutePath(), new SecurityException("read"), ErrorManager.OPEN_FAILURE); } } /** * Creates a common temp file prefix. * * @return the file prefix. */ private String prefixName() { return "FileErrorManager"; } /** * Creates a common temp file suffix. * * @return the file suffix. */ private String suffixName() { return ".eml"; } /** * Determines if the given message is a MIME message or just free text. * * @param msg the message to examine. * @return true if MIME message otherwise false. */ private boolean isRawEmail(String msg) { if (msg != null && msg.length() > 0) { return !msg.startsWith(Level.SEVERE.getName()); } return false; } /** * Stores the given string in a file. * * @param email the message to store. * @throws IOException if there is a problem. */ private void storeEmail(String email) throws IOException { File tmp = null; FileOutputStream out = null; for (;;) { tmp = File.createTempFile(prefixName(), suffixName(), emailStore); try { out = new FileOutputStream(tmp); break; } catch (FileNotFoundException FNFE) { if (!tmp.exists()) { //retry if file is locked throw FNFE; } } } try { //Raw email is ASCII. PrintStream ps = new PrintStream(wrap(out), false, "US-ASCII"); ps.print(email); ps.flush(); tmp = null; //Don't delete 'tmp' if all bytes were written. ps.close(); } finally { close(out); delete(tmp); //Only deletes if not null. } } /** * Null safe close method. * * @param out closes the given stream. */ private void close(OutputStream out) { if (out != null) { try { out.close(); } catch (IOException IOE) { super.error(out.toString(), IOE, ErrorManager.CLOSE_FAILURE); } } } /** * Null safe delete method. * * @param tmp the file to delete. */ private void delete(File tmp) { if (tmp != null) { try { if (!tmp.delete() && tmp.exists()) { try { try { tmp.deleteOnExit(); } catch (final LinkageError shutdown) { throw new RuntimeException(shutdown); } } catch (final RuntimeException shutdown) { if (!tmp.delete()) { super.error(tmp.getAbsolutePath(), shutdown, ErrorManager.CLOSE_FAILURE); } } } } catch (SecurityException SE) { super.error(tmp.toString(), SE, ErrorManager.CLOSE_FAILURE); } } } /** * Gets the location of the email store. * * @return the File location. */ private File getEmailStore() { String dir = manager.getProperty( getClass().getName().concat(".pattern")); if (dir == null) { dir = AccessController.doPrivileged(new PrivilegedAction() { public String run() { return System.getProperty("java.io.tmpdir", "."); } }); } return new File(dir); } /** * Wraps the given stream as a NewLineOutputStream. * * @param out the stream to wrap. * @return the original or wrapped output stream. */ private OutputStream wrap(OutputStream out) { assert out != null; Class k; try { k = Class.forName("NewlineOutputStream"); if (OutputStream.class.isAssignableFrom(k)) { Constructor c = k.getConstructor(OutputStream.class); return (OutputStream) c.newInstance(out); } else { super.error("Unable to switch newlines", new ClassNotFoundException(k.getName()), ErrorManager.GENERIC_FAILURE); } } catch (RuntimeException re) { super.error("Unable to switch newlines", re, ErrorManager.GENERIC_FAILURE); } catch (Exception ex) { super.error("Unable to switch newlines", ex, ErrorManager.GENERIC_FAILURE); } catch (LinkageError le) { super.error("Unable to switch newlines", new ClassNotFoundException("", le), ErrorManager.GENERIC_FAILURE); } return out; } }