Package org.jasen

Source Code of org.jasen.JasenScanner

/*
* @(#)JasenScanner.java  5/01/2005
*
* Copyright (c) 2004, 2005  jASEN.org
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*   1. Redistributions of source code must retain the above copyright notice,
*      this list of conditions and the following disclaimer.
*
*   2. 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.
*
*   3. The names of the authors may not be used to endorse or promote products
*      derived from this software without specific prior written permission.
*
*   4. Any modification or additions to the software must be contributed back
*      to the project.
*
*   5. Any investigation or reverse engineering of source code or binary to
*      enable emails to bypass the filters, and hence inflict spam and or viruses
*      onto users who use or do not use jASEN could subject the perpetrator to
*      criminal and or civil liability.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED 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 JASEN.ORG,
* OR ANY CONTRIBUTORS TO THIS SOFTWARE 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.
*
*/
package org.jasen;

import java.io.InputStream;
import java.util.List;

import javax.mail.internet.MimeMessage;

import org.apache.log4j.Logger;
import org.jasen.core.engine.Jasen;
import org.jasen.error.ErrorHandlerBroker;
import org.jasen.error.JasenException;
import org.jasen.event.JasenAutoUpdateListener;
import org.jasen.event.JasenScanListener;
import org.jasen.interfaces.JasenConfigurationLoader;
import org.jasen.interfaces.JasenMessage;
import org.jasen.interfaces.JasenScanResult;
import org.jasen.thread.ControlledThread;
import org.jasen.update.JasenAutoUpdateManager;
import org.jasen.update.JasenAutoUpdateParcel;
import org.jasen.update.JasenAutoUpdateParcelWrapper;
import org.jasen.update.JasenAutoUpdateReaper;
import org.jasen.update.JasenAutoUpdateReport;
import org.jasen.util.ReadOnlyList;
import org.jasen.util.ThreadUtils;


/**
* <p>
* JasenScanner is a singleton scanner class for safe scanning of email messages using the jASEN engine.
* </p>
* <p>
* All message scanning should be done through this class to guarantee thread safety and in particular to allow
* for correct behaviour during auto updates.
* </p>
* <p>
* It is not, however, mandatory that this class be used exclusively for scanning, merely recommended.
* <br/>
* For direct (non-singleton) access to the engine, use the org.jasen.core.engine.Jasen class
* </p>
* @see org.jasen.core.engine.Jasen
* @author Jason Polites
*/
public final class JasenScanner {

    private static JasenScanner instance;
    private static final Object instancelock = new Object();
    private static final Object scanlock = new Object();

    private boolean initialized = false;
    private volatile boolean updating = false;

    static Logger logger = Logger.getLogger(JasenScanner.class);

    private Jasen jasen;
    private Restarter restarter;

    private boolean alive = false;
    private boolean restartRequired = false;

    private volatile int scansInProgress = 0;

    private JasenScanListener scanListener;
    private JasenAutoUpdateListener autoUpdateListener;

   
    /**
     * <p>
     * The restarter polls for restart requests and restarts the engine when required
     * </p>
     */
    final class Restarter extends ControlledThread {
        /**
         *
         */
        public Restarter() {
            super();
        }
        /**
         * @param name
         */
        public Restarter(String name) {
            super(name);
        }
       
        public void handleException(Exception e) {
            jasen.getErrorHandler().handleException(e);
        }
        public void process() throws JasenException {
            if(restartRequired) {
                synchronized(scanlock) {
                    restartRequired = false;
                    try {
                        logger.debug("Restart required... Issueing engine restart...");
                       
                        jasen.restart();
                       
//                        if(autoUpdateListener != null) {
//                            autoUpdateListener.onAfterUpdate();
//                        }
                    }
                    finally {
                        scanlock.notifyAll();
                    }
                }
            }
        }
    }

    /**
     *
     */
    private JasenScanner() {
        super();
    }

    /**
     * Gets the current instance of the JasenScanner
     * @return The current (and only) JasenScanner instance
     */
    public static final JasenScanner getInstance() {
        if(instance == null) {
            synchronized(instancelock) {

                if(instance == null) {
                    instance = new JasenScanner();
                }

                instancelock.notifyAll();
            }
        }

        return instance;
    }

    /**
     * Gets the reference to the internal scan engine
     * @return The single Jasen instance
     */
    public Jasen getEngine() {
        return this.jasen;
    }
   
    /**
     * Initialises the engine with the default configuration
     * @throws JasenException
     * @see org.jasen.core.engine.Jasen#init()
     */
    public void init() throws JasenException {
        jasen = new Jasen();
        jasen.init();
        initInternal();
    }
   
    /**
     * Initialises the engine using the configuration loader provided
     * @param loader
     * @throws JasenException
     * @see org.jasen.core.engine.Jasen#init(JasenConfigurationLoader)
     */
    public void init(JasenConfigurationLoader loader) throws JasenException {
        jasen = new Jasen();
        jasen.init(loader);
        initInternal();
    }

    /**
     * Initialises the engine with the configuration file specified
     * @param config The absolute path to the configuration file
     * @throws JasenException
     * @see org.jasen.core.engine.Jasen#init(String)
     * @deprecated
     */
    public void init(String config) throws JasenException {
        jasen = new Jasen();
        jasen.init(config);
        initInternal();
    }

    /**
     * Initialises the engine with the configuration document passed as a stream
     * @param in
     * @throws JasenException
     * @see org.jasen.core.engine.Jasen#init(InputStream)
     * @deprecated
     */
    public void init(InputStream in) throws JasenException {
        jasen = new Jasen();
        jasen.init(in);
        initInternal();
    }
   
    private void initInternal() {
        alive = true;
        initialized = true;
       
        logger.debug("Starting engine restarter thread");
       
        restarter = new Restarter("Auto Update Restarter");
        restarter.start();
       
        // If we are supposed to update on start... force an update here
        if(JasenAutoUpdateManager.getInstance().getConfiguration() != null && JasenAutoUpdateManager.getInstance().getConfiguration().isCheckOnStartup()) {
            try {
                forceUpdate();
            } catch (JasenException e) {
                ErrorHandlerBroker.getInstance().getErrorHandler().handleException(e);
            }
        }
    }

    /**
     * Destroys the engine and all registered plugins
     */
    public synchronized void destroy() {
        if(alive) {
            jasen.destroy();

            logger.debug("Stopping engine restarter thread...");
           
            if(!ThreadUtils.forceFinish(restarter, JasenAutoUpdateReaper.killTimeout)) {
                logger.warn("Restarter thread in JasenScanner did not die and was killed");
            }
           
            alive = false;           
        }
    }
   
    /**
     * Scans the given message.
     * <p>
     *   All plugins will execute regardless of the probability (or computed total probability) discovered from any single plugin or combination thereof
     * </p>
     * @param mm The MimeMessage to be scanned
     * @return The results of the scan as a JasenScanResult
     * @throws JasenException
     */
    public JasenScanResult scan(MimeMessage mm) throws JasenException {
        return scan(mm, (String[])null);
    }


    /**
     * Scans the given message.
   *  <p>
   *     The threshold value indicates the value at which we know the message is spam without continuing
   *   </p>
     * If the engine computes this threshold prior to all plugins executing, tests are stopped and the result is
     * returned immediately
     * @param mm The MimeMessage to be scanned
     * @param threshold The marker above which scanning ceases
     * @return The results of the scan as a JasenScanResult
     * @throws JasenException
     * @see JasenScanner#scan(MimeMessage, JasenMessage, float)
     */
    public JasenScanResult scan(MimeMessage mm, float threshold) throws JasenException {
        return scan(mm, threshold, null);
    }

    /**
     * Scans the message without a threshold specified
     * @param mm The MimeMessage to be scanned
     * @param message A pre-parsed JasenMessage
     * @return The results of the scan as a JasenScanResult
     * @throws JasenException
     * @see JasenScanner#scan(MimeMessage, JasenMessage, float)
     */
    public JasenScanResult scan(MimeMessage mm, JasenMessage message) throws JasenException {
        return scan(mm, message, null);
    }


    /**
     * Scans the given mime message using the already mime-parsed JasenMessage.
     * <p>
     * This implementation allows calling applications to implement their own JasenMessage by passing it to the scan engine.
     * </p>
     * @param mm The MimeMessage to be scanned
     * @param message A pre-parsed JasenMessage
     * @param threshold The thresholds.  If any one plugin yields a result >= threshold, scanning is ceased and the result returned immediately
     * @return The results of the scan as a JasenScanResult
     * @throws JasenException
     * @see JasenScanner#scan(MimeMessage, float)
     */
    public JasenScanResult scan(MimeMessage mm, JasenMessage message, float threshold) throws JasenException {
        return scan(mm, message, threshold, null);
    }   
   

    /**
     * Scans the given message.
     * <p>
     *   All plugins will execute regardless of the probability (or computed total probability) discovered from any single plugin or combination thereof
     * </p>
     * @param mm The MimeMessage to be scanned
     * @param ignored A list of plugin names which will be ignored during the scan.  May be null
     * @return The results of the scan as a JasenScanResult
     * @throws JasenException
     */
    public JasenScanResult scan(MimeMessage mm, String[] ignored) throws JasenException {
        notifyScan();

        try {
            return jasen.scan(mm, ignored);
        }
        finally {
            notifyScanComplete();
        }
    }


    /**
     * Scans the given message.
   *  <p>
   *     The threshold value indicates the value at which we know the message is spam without continuing
   *   </p>
     * If the engine computes this threshold prior to all plugins executing, tests are stopped and the result is
     * returned immediately
     * @param mm The MimeMessage to be scanned
     * @param threshold The marker above which scanning ceases
     * @param ignored A list of plugin names which will be ignored during the scan.  May be null
     * @return The results of the scan as a JasenScanResult
     * @throws JasenException
     * @see JasenScanner#scan(MimeMessage, JasenMessage, float)
     */
    public JasenScanResult scan(MimeMessage mm, float threshold, String[] ignored) throws JasenException {

        notifyScan();

        try {
            return jasen.scan(mm, threshold, ignored);
        }
        finally {
            notifyScanComplete();
        }
    }

    /**
     * Scans the message without a threshold specified
     * @param mm The MimeMessage to be scanned
     * @param message A pre-parsed JasenMessage
     * @param ignored A list of plugin names which will be ignored during the scan.  May be null
     * @return The results of the scan as a JasenScanResult
     * @throws JasenException
     * @see JasenScanner#scan(MimeMessage, JasenMessage, float)
     */
    public JasenScanResult scan(MimeMessage mm, JasenMessage message, String[] ignored) throws JasenException {

        notifyScan();

        try {
            return jasen.scan(mm, message, ignored);
        }
        finally {
            notifyScanComplete();
        }

    }


    /**
     * Scans the given mime message using the already mime-parsed JasenMessage.
     * <p>
     * This implementation allows calling applications to implement their own JasenMessage by passing it to the scan engine.
     * </p>
     * @param mm The MimeMessage to be scanned
     * @param message A pre-parsed JasenMessage
     * @param threshold The thresholds.  If any one plugin yields a result >= threshold, scanning is ceased and the result returned immediately
     * @param ignored A list of plugin names which will be ignored during the scan.  May be null
     * @return The results of the scan as a JasenScanResult
     * @throws JasenException
     * @see JasenScanner#scan(MimeMessage, JasenMessage, float)
     */
    public JasenScanResult scan(MimeMessage mm, JasenMessage message, float threshold, String[] ignored) throws JasenException {

        notifyScan();

        try {
            return jasen.scan(mm, message, threshold, ignored);
        }
        finally {
            notifyScanComplete();
        }
    }

    /**
     * Records the occurrance of a scan
     * @throws JasenException
     */
    public void notifyScan() throws JasenException {

        synchronized(scanlock) {
            if(updating) {
                try {
                    logger.debug("Waiting to execute a scan...");
                    scanlock.wait();
                    logger.debug("Stopped waiting executing scan...");
                } catch (InterruptedException ignore) {}

                if(!initialized) {
                    throw new JasenException("Cannot access the jASEN engine until it has been intialized!");
                }
            }

            scansInProgress++;
            scanlock.notifyAll();
        }

        if(scanListener != null) {
            scanListener.onScanStart();
        }
    }

    /**
     * Records the completion of a scan
     * @throws JasenException
     */
    public void notifyScanComplete() throws JasenException {
        synchronized(scanlock) {
            scansInProgress--;
            scanlock.notifyAll();
        }
       
        if(scanListener != null) {
            scanListener.onScanEnd();
        }       
    }

    /**
     * Called by the AutoUpdateManager if there is an update pending to signal a pause in scanning
     *
     */
    public boolean notifyPendingUpdate(JasenAutoUpdateParcel parcel) {
       
        if((autoUpdateListener != null && autoUpdateListener.onBeforeUpdate())
             || autoUpdateListener == null) {
            logger.debug("Scanner notified of pending update.  Locking scan access...");
           
            synchronized(scanlock) {
                // Signal the intention to update
                updating = true;

                // Now wait for any current scans to complete...
                while(scansInProgress > 0) {
                    try {
                        scanlock.wait();
                    } catch (InterruptedException ignore) {}
                }
               
                scanlock.notifyAll();

                if(autoUpdateListener != null) {
                    return autoUpdateListener.onUpdateStart(new JasenAutoUpdateParcelWrapper(parcel));
                }
                else
                {
                    return true;
                }
            }           
        }
        else if(autoUpdateListener != null) {
            logger.debug("Auto update listener aborted update");
        }

        return false;

    }

    /**
     * Called by the AutoUpdateManager when the update has completed
     * @param report A report of the results of an update
     * @throws JasenException
     */
    public void notifyUpdateComplete(JasenAutoUpdateReport report) throws JasenException {

        synchronized(scanlock) {
           
            boolean restart = report.isEngineRestart();

            if(autoUpdateListener != null) {
                if(autoUpdateListener.onUpdateEnd(report) && report.isEngineRestart()) {
                    restart = true;
                }
            }       
           
            if(restart) {
                restartRequired = true;
                logger.debug("Waking up the restarter");
                restarter.wake();
            }

            updating = false;

            logger.info("Auto update completed successfully" + ((report.isEngineRestart()) ? ", restart pending." : ""));  
           
            // Notify waiting listeners
            if(autoUpdateListener != null) {
                autoUpdateListener.onAfterUpdate();
            }
           
            scanlock.notifyAll();
        }
    }

   
    public void notifyUpdateDownload(long bytes) {
        if(autoUpdateListener != null) {
            autoUpdateListener.onUpdateDownload(bytes);
        }
    }   
    /**
     * Returns true IFF the engine is initialised and ready for scans.
     * @return True if the engine is alive, false otherwise.
     */
    public boolean isAlive() {
        return alive;
    }

    /**
     * Gets the listener registered for scan events if one has been set.
     * @return The current listener, or null if no listener has been set
     */
    public JasenScanListener getScanListener() {
        return scanListener;
    }

    /**
     * Sets the listener which will record scan events.
     * @param scanListener
     */
    public void setScanListener(JasenScanListener scanListener) {
        this.scanListener = scanListener;
    }

    /**
     * Gets the listener registered for auto update events if one has been set.
     * @return The registered listener, or null if no listener has been set
     */
    public JasenAutoUpdateListener getAutoUpdateListener() {
        return autoUpdateListener;
    }

    /**
     * Sets the listener which will record update completion events.
     * @param autoUpdateListener
     */
    public void setAutoUpdateListener(JasenAutoUpdateListener autoUpdateListener) {
        this.autoUpdateListener = autoUpdateListener;
    }


    /**
     * Forces the engine to run an update check
     * @return If true, the force request was received and the engine is checking for
     * updates. If false, the engine is either not running, or is already in the process of checking
     * @throws JasenException
     */
    public boolean forceUpdate() throws JasenException {
       return JasenAutoUpdateManager.getInstance().forceUpdate();
    }
   
    /**
     * Returns a read-only list of the plugins registered in the engine.
     * <br/>
     * The list does not allow modification.  Any attempt to modify the list will
     * result in an UnsupportedOperationException
     * @return A list of PluginContainer objects containing the registered plugins
     * @see org.jasen.core.PluginContainer
     */
    public List getPlugins() {
        return new ReadOnlyList(jasen.getPlugins());
    }
}
TOP

Related Classes of org.jasen.JasenScanner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.