com.emarsys.dyson.DysonStorage.java Source code

Java tutorial

Introduction

Here is the source code for com.emarsys.dyson.DysonStorage.java

Source

/**
 *   (c) Copyright 2007-2010 by emarsys eMarketing Systems AG
 * 
 *   This file is part of dyson.
 *
 *   dyson is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation; either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   dyson is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.emarsys.dyson;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.emarsys.ecommon.util.Assertions;
import com.emarsys.ecommon.util.CollectionToStringBuilder;

/**
 * <p>
 * The {@link DysonStorage} is the service responsible for 
 * persistently storing messages delivered to dyson. 
 * </p><p>
 * {@link DysonStorage} serves as an abstract super class for all
 * possible implemenations.
 * </p><p>
 * Where the {@link DysonStorage} finally puts the mail files is 
 * up to its concrete implementation as well as its associated 
 * {@link MailStorageFileNamingScheme}.
 * <br/>
 * It's mandatory for all {@link DysonStorage} implementations to 
 * separate the locations where incoming mails are stored/buffered/cached 
 * from the place where the files are finally stored persistently.
 * Said locations are called the {@link #getIncomingDirName() incoming} 
 * and {@link #getProcessedDirName() processed} directories.
 * </p><p>
 * Like all {@link GenericDysonPart dyson parts} every implementation of
 * {@link DysonStorage} has to provide a public default constructor taking 
 * a {@link Dyson} instance.
 * </p><p>
 * 
 * </p>
 * 
 * @author <a href="mailto:kulovits@emarsys.com">Michael "kULO" Kulovits</a>
 */
public abstract class DysonStorage extends GenericDysonPart {
    private static final Logger log = LoggerFactory.getLogger(DysonStorage.class);

    /**
     * TODO implement an overall state for the dysonstorage
     * possible states (still to discuss):
     *  - accepting/idle //running, nothing to do
     *  - accepting/processing //running, currentyl (still processing mails)
     *  - finishing_up //still processing after a stop request, but not accepting more mails
     *  - stopped/not running
     */
    public enum State {

    }//enum State

    /**
     * factory method.
     * 
     * hooks for concrete subclasses: 
     *    - {@link #init()} <br/>
     *  - {@link #checkInitialization()}
     * 
     */
    public static DysonStorage getInstance(Dyson dyson) {
        DysonStorage storage = dyson.newDysonPart(DysonConfig.STORAGE_CLASS, DysonStorage.class);
        storage.init();
        storage.checkInitialization();
        return storage;
    }

    //cached settings
    protected String incomingDirName;
    protected String processedDirName;
    protected String mailFileSuffix;
    protected String mailPartialFileSuffix;

    //file system storage
    protected MailStorageFileNamingScheme namingScheme;

    /**
     * Default constructor.
     * 
     * All subclasses of {@link DysonStorage} must have a 
     * default public defaut constructor taking a {@link Dyson} instance.
     */
    public DysonStorage(Dyson dyson) {
        super(dyson);
    }

    /**
     * <p>
     * initializes this storage instance after it's creation.
     * </p><p>
     * if this methods is redefined in subclasses then it should
     * call it's super implementation as first statement unless
     * there's a good reason not to do so.
     * </p>
     */
    protected abstract void init();

    /**
     * <p>
     * initializes this storage instance after it's creation.
     * </p><p>
     * if this methods is redefined in subclasses then it should
     * call it's super implementation as first statement unless
     * there's a good reason not to do so.
     * </p>
     * 
     * @throws IllegalStateException
     */
    protected void checkInitialization() throws IllegalStateException {
        try {
            Assertions.assertNotEmpty(this.incomingDirName);
            Assertions.assertNotEmpty(this.processedDirName);
            Assertions.assertNotEmpty(this.mailFileSuffix);
            Assertions.assertNotEmpty(this.mailPartialFileSuffix);
            Assertions.assertNotNull(this.namingScheme);
        }
        //TODO refactor "not empty checks" in order to omit this exception wrapping
        catch (AssertionError ae) {
            throw new IllegalStateException("" + ae.getMessage(), ae);
        }
    }

    /**
     * 
     * @return
     */
    public String getIncomingDirName() {
        return this.incomingDirName;
    }

    public String getProcessedDirName() {
        return this.processedDirName;
    }

    public Collection<File> getIncomingMailFiles() {
        return this.getMailFiles(this.getIncomingDirName());
    }

    public Collection<File> getProcessedMailFiles() {
        return this.getMailFiles(this.getProcessedDirName());
    }

    @SuppressWarnings("unchecked")
    protected Collection<File> getMailFiles(String dirName) {
        File dir = new File(dirName);

        Collection<File> mailFiles = FileUtils.listFiles(dir, new String[] { this.getMailFileSuffix() }, true);

        if (log.isTraceEnabled()) {
            log.trace("got mail files in \'{}\': {}", dir.getAbsolutePath(),
                    new CollectionToStringBuilder().appendAll(Arrays.asList(mailFiles)));
        }

        return mailFiles;
    }

    /**
     * @return the mailFileSuffix
     */
    public String getMailFileSuffix() {
        return this.mailFileSuffix;
    }

    /**
     * @return the mailPartialFileSuffix
     */
    public String getMailPartialFileSuffix() {
        return this.mailPartialFileSuffix;
    }

    /**
     * 
     * @return
     */
    public MailStorageFileNamingScheme getProcessedMailFileNamingScheme() {
        return this.namingScheme;
    }

    /**
     * TODO documentation
     * @return
     */
    public abstract boolean isRunning();

    /**
     * TODO documentation
     */
    public abstract void start();

    /**
     * TODO documentation
     */
    public abstract void stop();

    /**
     * waits for this dysonstorage to terminate until the timeout exceeded
     * 
     * 
     * TODO proper specification
     * 
     * @param i
     * @param seconds
     */
    public abstract void awaitTermination(int timeOut, TimeUnit unit);

    /**
     * deletes all files in the {@link #getIncomingDirName() 
     * incoming directory}
     * @throws IOException 
     */
    public abstract void clearIncomingDir() throws IOException;

    /**
     * deletes all files in the {@link #getProcessedDirName()
     * processed directory}
     * @throws IOException 
     */
    public abstract void clearProcessedDir() throws IOException;

    /**
     * <p>
     * Submits a task as {@link Runnable} to be executed asynchronously 
     * by the {@link DysonStorage}.
     * </p>
     * @param task
     * @throws IllegalStateException - if the storage is not 
     *    {@link #isRunning() running}
     */
    public abstract void submitTask(Runnable task) throws IllegalStateException;

}//class DysonStorage