ome.formats.importer.util.ErrorHandler.java Source code

Java tutorial

Introduction

Here is the source code for ome.formats.importer.util.ErrorHandler.java

Source

/*
 *   $Id$
 *
 *   Copyright 2009 Glencoe Software, Inc. All rights reserved.
 *   Use is subject to license terms supplied in LICENSE.txt
 */

package ome.formats.importer.util;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import loci.formats.MissingLibraryException;
import ome.formats.importer.IObservable;
import ome.formats.importer.IObserver;
import ome.formats.importer.ImportCandidates;
import ome.formats.importer.ImportConfig;
import ome.formats.importer.ImportEvent;

import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Top of the error handling hierarchy. Will add errors to a queue
 * which can be sent with {@link #sendErrors()}. Subclasses will get
 * a change to handle all {@link ImportEvent} instances, but should
 * try not to duplicate handling.
 *
 * @author Brian W. Loranger
 * @author Josh Moore
 *
 * @since Beta4.1
 */
public abstract class ErrorHandler implements IObserver, IObservable {

    /**
     * @author Brian W. Loranger
     * @author Josh Moore
     */
    public abstract static class EXCEPTION_EVENT extends ImportEvent {
        public final Exception exception;

        /**
         * @param exception - set exception
         */
        public EXCEPTION_EVENT(Exception exception) {
            this.exception = exception;
        }
    }

    /**
     * @author Brian W. Loranger
     * @author Josh Moore
     */
    public static class INTERNAL_EXCEPTION extends EXCEPTION_EVENT {
        public final String filename;
        public final String[] usedFiles;
        public final String reader;

        public INTERNAL_EXCEPTION(String filename, Exception exception, String[] usedFiles, String reader) {
            super(exception);
            this.filename = filename;
            this.usedFiles = usedFiles;
            this.reader = reader;
        }

        /* (non-Javadoc)
         * @see ome.formats.importer.ImportEvent#toLog()
         */
        @Override
        public String toLog() {
            return String.format("%s: %s\n%s", super.toLog(), filename, getStackTrace(exception));
        }
    }

    /**
     * Unlike {@link FILE_EXECEPTION}, UKNOWN_FORMAT does not have a reader
     * since bio-formats is telling us that it does not know how to handle
     * the given file. This should be generally be considered less fatal
     * than a {@link FILE_EXCEPTION}, but if the user is specifically saying
     * that a file should be imported, and an {@link UNKNOWN_FORMAT} is raised,
     * then perhaps there is a configuration issue.
     *
     * @author Brian W. Loranger
     * @author Josh Moore
     */
    public static class UNKNOWN_FORMAT extends EXCEPTION_EVENT {
        public final String filename;
        public final Object source;

        /**
         * @param filename
         * @param exception
         * @param source
         */
        public UNKNOWN_FORMAT(String filename, Exception exception, Object source) {
            super(exception);
            this.filename = filename;
            this.source = source;
        }

        /* (non-Javadoc)
         * @see ome.formats.importer.ImportEvent#toLog()
         */
        @Override
        public String toLog() {
            return super.toLog() + ": " + filename;
        }
    }

    /**
     * Similar to {@link UKNOWN_FORMAT} UNREADABLE_FILE specifies that the
     * file which is being accessed is unreadable (does not exist or canRead
     * is false), so if the user is specifically saying that the file should
     * be imported, there may be some underlying issue.
     *
     * @author Brian W. Loranger
     * @author Josh Moore
     */
    public static class UNREADABLE_FILE extends EXCEPTION_EVENT {
        public final String filename;
        public final Object source;

        /**
         * @param filename
         * @param exception
         * @param source
         */
        public UNREADABLE_FILE(String filename, Exception exception, Object source) {
            super(exception);
            this.filename = filename;
            this.source = source;
        }

        /* (non-Javadoc)
         * @see ome.formats.importer.ImportEvent#toLog()
         */
        @Override
        public String toLog() {
            return super.toLog() + ": " + filename;
        }
    }

    /**
     * {@link FILE_EXCEPTION}s are thrown any time in the context of a particular
     * file and otherwise unspecified exception takes place. An example of an
     * exception which receives separate handling is {@link UKNOWN_FORMAT} which
     * can be considered less serious than {@link FILE_EXCEPTION}. Subclasses of
     * this class may should receive special handling. For example,
     * {@link ImportCandidates#SCANNING_FILE_EXCEPTION} may be considered less
     * significant if the user was trying to import a large directory.
     * {@link MISSING_LIBRARY} below is probably more of a warn situation rather
     * than an error.
     */
    public static class FILE_EXCEPTION extends EXCEPTION_EVENT {
        public final String filename;
        public final String[] usedFiles;
        public final String reader;

        public FILE_EXCEPTION(String filename, Exception exception, String[] usedFiles, String reader) {
            super(exception);
            this.filename = filename;
            this.usedFiles = usedFiles;
            this.reader = reader;
        }

        @Override
        public String toLog() {
            //this.exception.printStackTrace();
            return super.toLog() + ": " + filename;
        }
    }

    /**
     * A {@link FILE_EXCEPTION} caused specifically by some library (native
     * or otherwise) not being installed locally.
     */
    public static class MISSING_LIBRARY extends FILE_EXCEPTION {
        public MISSING_LIBRARY(String filename, MissingLibraryException exception, String[] usedFiles,
                String reader) {
            super(filename, exception, usedFiles, reader);
        }
    }

    final protected Log log = LogFactory.getLog(getClass());

    final protected ArrayList<IObserver> observers = new ArrayList<IObserver>();

    final protected ArrayList<ErrorContainer> errors = new ArrayList<ErrorContainer>();

    final protected ImportConfig config;

    protected boolean cancelUploads = false;

    protected boolean sendFiles = true;

    protected boolean sendLogs = true;

    public boolean fileUploadErrors = false;

    protected int totalErrors = 0;

    private FileUploader fileUploader;

    /**
     * Initialize
     *
     * @param config
     */
    public ErrorHandler(ImportConfig config) {
        this.config = config;
    }

    /* (non-Javadoc)
     * @see ome.formats.importer.IObserver#update(ome.formats.importer.IObservable, ome.formats.importer.ImportEvent)
     */
    public final void update(IObservable observable, ImportEvent event) {

        if (event instanceof MISSING_LIBRARY) {
            MISSING_LIBRARY ev = (MISSING_LIBRARY) event;
            log.warn(ev.toLog(), ev.exception);
        }

        else if (event instanceof FILE_EXCEPTION) {
            FILE_EXCEPTION ev = (FILE_EXCEPTION) event;
            log.error(ev.toLog(), ev.exception);
            addError(ev.exception, new File(ev.filename), ev.usedFiles, ev.reader);
        }

        else if (event instanceof INTERNAL_EXCEPTION) {
            INTERNAL_EXCEPTION ev = (INTERNAL_EXCEPTION) event;
            log.error(event.toLog(), ev.exception);
            addError(ev.exception, new File(ev.filename), ev.usedFiles, ev.reader);
        }

        else if (event instanceof UNKNOWN_FORMAT) {
            UNKNOWN_FORMAT ev = (UNKNOWN_FORMAT) event;
            String[] usedFiles = { ev.filename };
            // Here it is important to not report errors which
            // are coming from ImportCandidates, since that doesn't
            // count as an error situation. Previously, this checked
            // for (ev.source instanceof ImportLibrary), but that is
            // no longer on the compile-time classpath.
            if (!(ev.source instanceof ImportCandidates))
                addError(ev.exception, new File(ev.filename), usedFiles, "");
            log.debug(event.toLog());
        }

        else if (event instanceof EXCEPTION_EVENT) {
            EXCEPTION_EVENT ev = (EXCEPTION_EVENT) event;
            log.error(ev.toLog(), ev.exception);
        }

        onUpdate(observable, event);

    }

    /**
     * @return number of errors in ErrorContainer array
     */
    public int errorCount() {
        return errors.size();
    }

    /**
     * abstract on update method
     *
     * @param importLibrary
     * @param event - importEvent
     */
    protected abstract void onUpdate(IObservable importLibrary, ImportEvent event);

    /**
     * Send existing errors in ErrorContainer array to server
     */
    protected void sendErrors() {

        for (int i = 0; i < errors.size(); i++) {

            if (!isSend(i)) {
                onSent(i);
                continue; // Don't send file if not selected
            }

            if (cancelUploads) {
                onCancel();
                break;
            }

            ErrorContainer errorContainer = errors.get(i);
            if (errorContainer.getStatus() != -1) // if file not pending, skip
                // it
                continue;

            List<Part> postList = new ArrayList<Part>();

            postList.add(new StringPart("java_version", errorContainer.getJavaVersion()));
            postList.add(new StringPart("java_classpath", errorContainer.getJavaClasspath()));
            postList.add(new StringPart("app_version", errorContainer.getAppVersion()));
            postList.add(new StringPart("comment_type", errorContainer.getCommentType()));
            postList.add(new StringPart("os_name", errorContainer.getOSName()));
            postList.add(new StringPart("os_arch", errorContainer.getOSArch()));
            postList.add(new StringPart("os_version", errorContainer.getOSVersion()));
            postList.add(new StringPart("extra", errorContainer.getExtra()));
            postList.add(new StringPart("error", getStackTrace(errorContainer.getError())));
            postList.add(new StringPart("comment", errorContainer.getComment()));
            postList.add(new StringPart("email", errorContainer.getEmail()));
            postList.add(new StringPart("app_name", "2"));
            postList.add(new StringPart("import_session", "test"));
            postList.add(new StringPart("absolute_path", errorContainer.getAbsolutePath() + "/"));

            String sendUrl = config.getTokenUrl();

            if (isSend(i)) {
                postList.add(new StringPart("selected_file", errorContainer.getSelectedFile().getName()));
                postList.add(new StringPart("absolute_path", errorContainer.getAbsolutePath()));

                if (sendLogs) {
                    errorContainer.addFile(config.getLogFile());
                }

                String[] files = errorContainer.getFiles();

                if (files != null && files.length > 0) {
                    for (String f : errorContainer.getFiles()) {
                        File file = new File(f);
                        postList.add(new StringPart("additional_files", file.getName()));
                        if (file.getParent() != null)
                            postList.add(new StringPart("additional_files_path", file.getParent() + "/"));
                        postList.add(new StringPart("additional_files_size", ((Long) file.length()).toString()));
                    }
                }
            }

            try {
                HtmlMessenger messenger = new HtmlMessenger(sendUrl, postList);
                String serverReply = messenger.executePost();

                // TODO add code here for sendLogs
                if (sendFiles) {
                    onSending(i);
                    errorContainer.setToken(serverReply);

                    fileUploader = new FileUploader(messenger.getHttpClient());
                    fileUploader.addObserver(this);

                    fileUploader.uploadFiles(config.getUploaderUrl(), 2000, errorContainer);
                    onSent(i);
                } else {
                    onNotSending(i, serverReply);
                }
            } catch (Exception e) {
                log.error("Error while sending error information.", e);
                onException(e);
            }

        }
        if (cancelUploads) {
            finishCancelled();
        }
        if (fileUploadErrors) {
            finishWithErroredFiles();
            notifyObservers(new ImportEvent.ERRORS_COMPLETE());
        } else {
            finishComplete();
            notifyObservers(new ImportEvent.ERRORS_COMPLETE());
        }
    }

    /**
     * Add detailed error to error container array
     * @param error - error thrown
     * @param file - head file for error
     * @param files - all files in import collection
     * @param readerType - reader type supplied from bio-formats
     */
    protected void addError(Throwable error, File file, String[] files, String readerType) {
        ErrorContainer errorContainer = new ErrorContainer();
        errorContainer.setFiles(files);
        errorContainer.setSelectedFile(file);
        errorContainer.setReaderType(readerType);
        errorContainer.setCommentType("2");

        errorContainer.setJavaVersion(System.getProperty("java.version"));
        errorContainer.setJavaClasspath(System.getProperty("java.class.path"));
        errorContainer.setOSName(System.getProperty("os.name"));
        errorContainer.setOSArch(System.getProperty("os.arch"));
        errorContainer.setOSVersion(System.getProperty("os.version"));
        errorContainer.setAppVersion(config.getVersionNumber());
        errorContainer.setError(error);
        addError(errorContainer);
    }

    /**
     * add simple error to error container array
     *
     * @param errorContainer
     */
    private void addError(ErrorContainer errorContainer) {
        String errorMessage = errorContainer.getError().toString();
        String[] splitMessage = errorMessage.split("\n");

        errorMessage = errorMessage.replaceAll("\n", "<br>&nbsp;&nbsp;");

        errorContainer.setIndex(totalErrors);
        totalErrors = totalErrors + 1;
        errorContainer.setStatus(-1); // pending status

        errors.add(errorContainer);
        onAddError(errorContainer, splitMessage[0]);
        notifyObservers(new ImportEvent.ERRORS_PENDING());
    }

    protected void clearErrors(int index) {
        errors.remove(index);
    }

    //
    // OBSERVER PATTERN
    //

    /* (non-Javadoc)
     * @see ome.formats.importer.IObservable#addObserver(ome.formats.importer.IObserver)
     */
    public final boolean addObserver(IObserver object) {
        return observers.add(object);
    }

    /* (non-Javadoc)
     * @see ome.formats.importer.IObservable#deleteObserver(ome.formats.importer.IObserver)
     */
    public final boolean deleteObserver(IObserver object) {
        return observers.remove(object);

    }

    /* (non-Javadoc)
     * @see ome.formats.importer.IObservable#notifyObservers(ome.formats.importer.ImportEvent)
     */
    public final void notifyObservers(ImportEvent event) {
        for (IObserver observer : observers) {
            observer.update(this, event);
        }
    }

    //
    // OVERRIDEABLE METHODS
    //

    /**
     * action to take on cancel
     */
    protected void onCancel() {
        fileUploader.cancel();
    }

    /**
     * Action to take on adding an error to container
     *
     * @param errorContainer - error container
     * @param message - message string for action (if needed)
     */
    protected void onAddError(ErrorContainer errorContainer, String message) {
    }

    /**
     * Check if files need sending at error container index
     * @param index - index in error container
     * @return - true if file is to be sent
     */
    protected boolean isSend(int index) {
        if (errors.get(index).getSelectedFile() == null) {
            return false;
        }
        return true;
    }

    /**
     * @param index
     */
    protected void onSending(int index) {
    }

    /**
     * @param index
     */
    protected void onSent(int index) {
    }

    /**
     * @param index
     * @param serverReply
     */
    protected void onNotSending(int index, String serverReply) {
    }

    /**
     * Action to take on exception
     * @param exception
     */
    protected void onException(Exception exception) {
        notifyObservers(new ImportEvent.ERRORS_FAILED());
    }

    /**
     * Action to take when finish cancelled
     */
    protected void finishCancelled() {
        fileUploader.cancel();
    }

    /**
     * Action to take when finish completed
     */
    protected void finishComplete() {
    }

    /**
     * Action to take when finish completed but with some errors
     * (For example, missing files)
     */
    protected void finishWithErroredFiles() {
    }

    /**
     * Return stack trace from throwable
     * @param throwable
     * @return stack trace
     */
    public static String getStackTrace(Throwable throwable) {
        final Writer writer = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(writer);
        throwable.printStackTrace(printWriter);
        return writer.toString();
    }

}