About us
Products
Services
Articles
Contact us

15. Mail Monitor

Contents | Previous Chapter | Next Chapter

This chapter describes the MailMonitor class, which invokes the connector's methods and deals with the multithreading issues.


15.1. Constructor and Methods

The constructor takes a long list of parameters whose values are saved to instance variables, creates an error handler and calls the connector's initialize() method.

The loop() method tries to connect to the mail store. If the operation succeeds, the connector's process() method is invoked in a loop. Then the wait() method inherited from Object is called. If the interruptionRequest flag was set, the disconnect() method is called and the loop() method returns the control to its caller, which is the run() method of the monitor's thread.

The start() method creates and starts the monitor's thread whose run() method calls loop(). If the monitor's thread was already started and an interruption request was made, the start() method cancels that request.

The stop() method requests the monitor's thread to interrupt itself. This is done by setting the interruptionRequest flag. The notifyAll() method inherited from Object is invoked in order to interrupt the wait() method that might have been called from loop(). The shutdown() method of the monitor's error handler calls stop(). That means that any fatal error will stop the monitor's thread.

If the thread monitor is active and no interruption request was made, processNow() uses notifyAll() to interrupt any possible call of wait() from loop(). Otherwise, it calls start() and stop() which ensures that the process() method will be invoked exactly one time supposing that the connection to the mail store can be established.

The join() method calls the method with the same name of the monitor's thread. That means that the thread that called join() will wait until the monitor's thread stops and then the thread that called join() will continue its execution.

MailMonitor.java:

package com.devsphere.apps.mapping.monitor;

import com.devsphere.helpers.logging.*;
import com.devsphere.logging.*;
import com.devsphere.mapping.*;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;

/**
 * Monitors a given mailbox for new mail and maps the messages to bean objects
 * that are forwarded to a processor.
 */
public class MailMonitor {
    // Monitor paramters
    protected Class beanClass;
    protected Object processor;
    protected Method method;
    protected String protocol;
    protected String host;
    protected String user;
    protected String password;
    protected String folderName;
    protected int seconds;
    protected AbstractLogger logger;
    protected ResourceBundle monitorRes;
    protected StoreConnector connector;
    protected ApplicationErrorHandler errorHandler;

    // Monitor state
    private Thread thread;
    private boolean interruptionRequest;

    /**
     * Creates the mail monitor
     */
    public MailMonitor(Class beanClass, Object processor, Method method,
        String protocol, String host, String user, String password,
        String folderName, int seconds, boolean debug, AbstractLogger logger,
        ResourceBundle monitorRes, StoreConnector connector) {
        thread = null;
        interruptionRequest = false;
        this.beanClass = beanClass;
        this.processor = processor;
        this.method = method;
        this.protocol = protocol;
        this.host = host;
        this.user = user;
        this.password = password;
        this.folderName = folderName;
        this.seconds = seconds;
        this.logger = logger;
        this.monitorRes = monitorRes;
        this.connector = connector;

        // Create the error handler
        errorHandler = new ApplicationErrorHandler(monitorRes, logger) {
            protected void shutdown() {
                stop();
            }
        };

        // Initialize the connector
        connector.initialize(beanClass, processor, method, debug, logger,
            errorHandler);
    }

    /**
     * If the connect() fails, this method brings the monitor
     * in its initial state. Otherwise it calls process() in a loop.
     */
    protected void loop() {
        boolean connected
            = connector.connect(protocol, host, user, password, folderName);
        if (!connected) {
            // Do the cleaning even if the connection failed
            connector.disconnect();
            synchronized (this) {
                thread = null;
                interruptionRequest = false;
            }
        } else {
            errorHandler.trace("[CONNECTED]");
            int milisec = seconds * 1000;
            while (true) {
                errorHandler.trace("[BEGIN_PROCESSING]");
                // Get and process the new messages
                connector.process();
                errorHandler.trace("[END_PROCESSING]");
                synchronized (this) {
                    if (!interruptionRequest)
                        try {
                            // This is called instead of Thread.sleep()
                            // It has the advantage that it can be interrupted
                            // by notifyAll().
                            wait(milisec);
                        } catch (InterruptedException e) {
                            // This exception shouldn't occur
                            // because thread.interrupt() isn't used
                            errorHandler.error(null, e);
                        }
                    if (interruptionRequest) {
                        connector.disconnect();
                        errorHandler.trace("[DISCONNECTED]");
                        thread = null;
                        break;
                    }
                }
            }
        }
    }

    /**
     * Creates and starts the monitor's thread whose run() method calls loop().
     * If the monitor's thread was already started, this method makes sure that
     * the thread will continue its execution even if an interruption
     * was requested by stop().
     */
    public synchronized void start() {
        if (thread == null) {
            thread = new Thread() {
                public void run() {
                    loop();
                }
            };
            interruptionRequest = false;
            errorHandler.trace("[THREAD_START]");
            thread.start();
        } else
            if (interruptionRequest) {
                errorHandler.trace("[THREAD_CANCEL_INTERRUPTION_REQUEST]");
                interruptionRequest = false;
            }
    }

    /**
     * Requests the monitor's thread to interrupt itself. This is done
     * by setting the interruptionRequest flag to true.
     * The thread.interrupt() call wouldn't have been safe since
     * it can interrupt any method that runs within the monitor's thread,
     * including process(), which does I/O that may throw InterruptedIOException
     * That's why the mail monitor uses the interruptionRequest flag.
     */
    public synchronized void stop() {
        if (thread != null && !interruptionRequest) {
            errorHandler.trace("[THREAD_INTERRUPTION_REQUEST]");
            interruptionRequest = true;
            notifyAll();
        }
    }

    /**
     * If the monitor isn't processing the new messages, processNow()
     * makes sure that the process() method is called supposing that
     * that the connection is/can be established. It lets the monitor
     * in the same state (started or stopped).
     */
    public synchronized void processNow() {
        if (thread != null && !interruptionRequest)
            notifyAll();
        else {
            start();
            stop();
        }
    }

    /**
     * Joins the current thread to the monitor's thread.
     */
    public void join() {
        Thread thread = this.thread;
        if (thread != null)
            try {
                int timeout = 10 * seconds * 1000;
                thread.join(timeout);
            } catch (InterruptedException e) {
            }
    }

}

15.2. Hints and Tips

The previous chapter presents the store connectors.

The next chapter describes the MonitorStarter class, which creates the MailMonitor instance.

Contents | Previous Chapter | Next Chapter

Copyright © 2000-2020 Devsphere

About us
Products
Services
Articles
Contact us