com.stimulus.archiva.incoming.IAPRunnable.java Source code

Java tutorial

Introduction

Here is the source code for com.stimulus.archiva.incoming.IAPRunnable.java

Source

package com.stimulus.archiva.incoming;

/* Copyright (C) 2005-2008 Jamie Angus Band 
 * MailArchiva Open Source Edition Copyright (c) 2005-2007 Jamie Angus Band
 * This program is free software; you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

import java.net.UnknownHostException;
import java.security.Security;
import java.util.*;

import com.stimulus.archiva.domain.*;
import javax.mail.*;
import java.util.*;
import javax.mail.search.*;
import org.apache.commons.logging.*;
import java.io.*;
import com.stimulus.archiva.exception.ArchivaException;
import com.stimulus.archiva.exception.ArchiveException;
import com.sun.mail.imap.*;
import com.sun.mail.imap.protocol.*;
import javax.mail.event.*;
import com.stimulus.util.*;

import java.util.concurrent.*;

public class IAPRunnable extends Thread {

    private static Log logger = LogFactory.getLog(IAPRunnable.class);
    protected URLName url;
    protected javax.mail.Store store = null;
    protected Folder inboxFolder = null;
    protected boolean shutdown = false;
    protected int intervalSecs = 0;
    protected FetchMessageCallback callback;
    protected String ipAddress = "";
    protected boolean test = false;
    protected IAPTestCallback testCallback;
    protected MailboxConnection connection;
    protected static int MAX_MESSAGES_TO_PROCESS = Config.getConfig().getMailboxConnections().getMaxMessages();
    protected static int DEAD_PERIOD = 6000000;
    private final String DUMMY_SSL_FACTORY = "com.stimulus.archiva.security.DummySSLSocketFactory";
    private final String SSL_FACTORY = "com.stimulus.archiva.security.MailArchivaSSLSocketFactory";
    private static final int IDLE_TIMEOUT = 300000; // 5 minutes
    protected ExecutorService archivePool = Executors
            .newFixedThreadPool(Config.getConfig().getArchiver().getArchiveThreads());

    static {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
    }

    public IAPRunnable(String threadName, IAPTestCallback testCallback, MailboxConnection connection,
            int intervalSecs, FetchMessageCallback callback) {
        logger.debug("iaprunnable constructor called");
        setDaemon(true);
        setName(threadName);
        this.intervalSecs = intervalSecs;
        this.callback = callback;
        this.testCallback = testCallback;
        this.connection = connection;
        setPriority(Thread.NORM_PRIORITY);
    }

    public MailboxConnection getMailboxConnection() {
        return connection;
    }

    public String getSSLFactory() {
        return DUMMY_SSL_FACTORY;
    }

    public void run() {

        try {
            java.net.InetAddress inetAdd = java.net.InetAddress.getByName(connection.getServerName());
            ipAddress = inetAdd.getHostAddress();

        } catch (UnknownHostException uhe) {
            logger.debug("failed to resolve address of mail server {connection.getServerName()='"
                    + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                    + connection.getUsername() + ",protocol='" + connection.getProtocol() + "'}");
            testOutput("failed to resolve ip address of mail server " + connection.getServerName());

        }

        if (ipAddress == null) {
            ipAddress = "unknown";
        }

        shutdown = false;

        while (!shutdown) {

            MAX_MESSAGES_TO_PROCESS = Config.getConfig().getMailboxConnections().getMaxMessages();
            logger.debug("connecting to mail server {serverName='" + connection.getServerName() + "',port='"
                    + connection.getPort() + "',userName='" + connection.getUsername() + ",protocol='"
                    + connection.getProtocol() + "'}");
            testOutput("connecting to mail server " + connection.getServerName());
            try {
                connect();
            } catch (Throwable e) {
                logger.error("failed to connect to mail server {serverName='" + connection.getServerName()
                        + "',port='" + connection.getPort() + "',userName='" + connection.getUsername()
                        + "',protocol='" + connection.getProtocol() + "'}. will retry in 1 second.", e);
                if (e.getCause() instanceof AuthenticationFailedException)
                    testOutput("could not authenticate with mail server " + connection.getServerName()
                            + ". username and/or password incorrect?");
                else {
                    String errormessage = "";
                    if (e.getCause() != null && e.getCause().getMessage() != null)
                        errormessage = e.getCause().getMessage();
                    testOutput("failed to connect to mail server " + connection.getServerName() + ". "
                            + e.getMessage() + "[" + errormessage + "]");
                }
                if (testCallback != null) {
                    break;
                } else {
                    try {
                        disconnect();
                    } catch (Exception ed) {
                    }
                    try {
                        Thread.sleep(intervalSecs);
                    } catch (Exception e2) {
                    }
                    continue;
                }
            }

            logger.debug("connected to  mail server {serverName='" + connection.getServerName() + "',port='"
                    + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
            testOutput("connected to mail server " + connection.getServerName());
            if (testCallback == null) {
                if (Compare.equalsIgnoreCase(connection.getProtocol().toString(), "IMAP") && connection.getIdle()) {
                    imapIdleMessages();
                    logger.debug("imap idle messages return()");
                } else {
                    pollMessages();
                }
            }
            try {
                disconnect();
            } catch (Exception e) {
                logger.error("failed to disconnect from mail server:" + e.getMessage() + "{ serverName='"
                        + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                        + connection.getUsername() + "'}", e);
                testOutput("failed to disconnect from mail server " + connection.getServerName());
            } catch (Throwable t) {
                logger.error("failed to disconnect from mail server:" + t.getMessage() + "{ serverName='"
                        + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                        + connection.getUsername() + "'}", t);
                testOutput("failed to disconnect from mail server " + connection.getServerName());
            }
            if (testCallback == null
                    && !(connection.getProtocol() == MailboxConnections.Protocol.IMAP && connection.getIdle())) {
                logger.debug("iap sleep {time='" + (intervalSecs * 1000) + "'}");
                try {
                    Thread.sleep(intervalSecs * 1000);
                } catch (Exception e) {
                }
            }
            if (testCallback != null) {
                testOutput("test complete.");
                break;
            }
        }
        if (testCallback == null && logger != null) {
            logger.warn("the iap service is shutdown {shutdown='" + shutdown + "'}");
        }
    }

    public void connect() throws Exception {
        /*
        if (store!=null) {
        if (store.isConnected())
        return; // already connected
        }*/

        Properties props = new Properties();
        String protocol = connection.getProtocol().toString().toLowerCase(Locale.ENGLISH);
        if (protocol.equals("pop"))
            protocol = "pop3";

        String server = connection.getServerName();
        String username = connection.getUsername();
        String password = connection.getPassword();
        int port = connection.getPort();
        int secureport = connection.getSSLPort();

        if (connection.getConnectionMode() == MailboxConnections.ConnectionMode.INSECURE) {
        } else if (connection.getConnectionMode() == MailboxConnections.ConnectionMode.FALLBACK) {
            props.put("mail." + protocol + ".starttls.enable", "true");
            props.put("mail." + protocol + ".socketFactory.fallback", "true");
            props.put("mail." + protocol + ".startTLS.socketFactory.class", getSSLFactory());
        } else if (connection.getConnectionMode() == MailboxConnections.ConnectionMode.TLS) {
            props.put("mail." + protocol + ".starttls.enable", "true");
            props.put("mail." + protocol + ".socketFactory.fallback", "false");
            props.put("mail." + protocol + ".startTLS.socketFactory.class", getSSLFactory());
        } else if (connection.getConnectionMode() == MailboxConnections.ConnectionMode.SSL) {
            protocol = protocol + "s";
            port = secureport;
            props.put("mail." + protocol + ".socketFactory.class", getSSLFactory());
            props.put("mail." + protocol + ".socketFactory.port", secureport);
            props.put("mail." + protocol + ".socketFactory.fallback", "false");
        }
        props.put("mail." + protocol + ".connectionpoolsize", 10);
        establishConnection(protocol, server, port, username, password, props);
    }

    public void establishConnection(String protocol, String server, int port, String username, String password,
            Properties props) throws ArchivaException {
        logger.debug("establishConnection() protocol='" + protocol + "',server='" + server + "',port='" + port
                + "',username='" + username + "',password='********'}");
        Session session = Session.getInstance(props, null);
        if (System.getProperty("mailarchiva.mail.debug") != null)
            session.setDebug(true);

        try {
            logger.debug("iap connect " + props);
            store = session.getStore(protocol);
        } catch (Throwable nspe) {
            logger.error("failed to retrieve iap store object:" + nspe, nspe);
            return;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("mailbox connection properties " + props);
        }
        try {
            store.connect(server, port, username, password);
        } catch (AuthenticationFailedException e) {
            logger.error("cannot connect to mail server. authentication failed {" + props + "}");
            throw new ArchivaException("unable to connect to mail server. could not authenticate. {" + props + "}",
                    e, logger);
        } catch (IllegalStateException ise) {
            throw new ArchivaException("attempt to connect mail server when it already connected. {" + props + "}",
                    ise, logger);
        } catch (MessagingException me) {
            if (me.getMessage().contains("sun.security.validator.ValidatorException")) {
                throw new ArchivaException(
                        "failed to authenticate TLS certificate. You must install the mail server's certificate as per the administration guide.",
                        me, logger);
            } else if (connection.getConnectionMode() == MailboxConnections.ConnectionMode.FALLBACK
                    && me.getMessage().contains("javax.net.ssl.SSLHandshakeException")) {
                logger.debug("cannot establish SSL handshake with mail server. falling back to insecure. {" + props
                        + "}");
                connection.setConnectionMode(MailboxConnections.ConnectionMode.INSECURE);
            } else
                throw new ArchivaException(
                        "failed to connect to mail server. " + me.getMessage() + ". {" + props + "}", me, logger);
        } catch (Throwable e) {
            throw new ArchivaException("unable to connect to mail server:" + e.getMessage(), e, logger);
        }
        try {
            inboxFolder = store.getDefaultFolder();
        } catch (Throwable e) {
            throw new ArchivaException("unable to get default folder. ", e, logger);
        }

        if (inboxFolder == null) {
            throw new ArchivaException("there was no default POP inbox folder found.", logger);
        }

        try {
            inboxFolder = inboxFolder.getFolder("INBOX");
            if (inboxFolder == null) {
                throw new ArchivaException("the inbox folder does not exist.", logger);
            }
        } catch (Throwable e) {
            throw new ArchivaException("unable to get INBOX folder. ", e, logger);
        }
        try {
            inboxFolder.open(Folder.READ_WRITE);
        } catch (Throwable e) {
            throw new ArchivaException("unable to open folder. ", e, logger);
        }
        return;
    }

    public boolean pollMessages() {
        logger.debug("iap pollmessages() {mailboxworker='" + getName() + "'}");
        boolean complete = true;
        try {
            Message[] messages;
            if (Compare.equalsIgnoreCase(connection.getProtocol().toString(), "IMAP") && connection.getUnread()) {
                messages = inboxFolder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
            } else {
                messages = inboxFolder.getMessages();
            }
            logger.debug("iap archivemesssages() called");
            int noMessages = archiveMessages(messages);
            if (noMessages < messages.length) {
                complete = false;
            }
            logger.debug("iap archivemesssages() end");
        } catch (IndexOutOfBoundsException iobe) {
            logger.error("mail server start and end message numbers are out of range {serverName='"
                    + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                    + connection.getUsername() + "'}");
            testOutput("mail server start and end message numbers are out of range");
        } catch (Throwable t) {
            logger.error("failed to retrieve messages during polling operation:" + t.getMessage(), t);
            testOutput("failed to retrieve messages during polling operation:" + t.getMessage());
        }

        if (testCallback != null) { // we're just testing 
            testOutput("retrieved messages from inbox");
            testOutput("test successful");
            shutdown = true;

        }
        return complete;
    }

    public void imapIdleMessages() {
        logger.debug("iap imapidlemessages() {mailboxworker='" + getName() + "'}");
        inboxFolder.addMessageCountListener(new MessageCountAdapter() {
            public void messagesAdded(MessageCountEvent ev) {
                Message[] messages = null;
                try {
                    if (store.isConnected()) {
                        messages = ev.getMessages();
                    } else {
                        return;
                    }

                } catch (IllegalStateException ise) {
                    logger.error("mail server inbox folder is not opened {serverName='" + connection.getServerName()
                            + "',port='" + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
                    testOutput("mail server inbox folder is not opened {serverName='" + connection.getServerName()
                            + "',port='" + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
                    return;
                } catch (IndexOutOfBoundsException iobe) {
                    logger.error("mail server start and end message numbers are out of range {serverName='"
                            + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                            + connection.getUsername() + "'}");
                    testOutput("mail server start and end message numbers are out of range {serverName='"
                            + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                            + connection.getUsername() + "'}");
                    return;
                } catch (Throwable e) {
                    logger.error("error occurred while idling on IMAP folder:" + e.getMessage() + " {serverName='"
                            + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                            + connection.getUsername() + "'}");
                }

                if (messages.length < 1) {
                    logger.debug("idle messages: no messages to process");
                    return;
                }
                archiveMessages(messages);

            }
        });

        boolean complete = false;
        long safety = 0;

        while (!complete && safety < 100000000) {
            complete = pollMessages();
            safety++;
        }

        while (!shutdown && inboxFolder.isOpen()) {
            try {
                logger.debug("idling to IMAP server {serverName='" + connection.getServerName() + "',port='"
                        + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
                ((IMAPFolder) inboxFolder).idle();
                logger.debug("return from idling with IMAP server {serverName='" + connection.getServerName()
                        + "',port='" + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
            } catch (FolderClosedException fce) {
                logger.debug("imap folder closed. possibly due to server timeout.");
                break;
            } catch (java.lang.IllegalStateException se) {
                logger.debug("idling has stopped. it is likely that the mailbox connection was shutdown:"
                        + se.getMessage());
                break;
            } catch (MessagingException me) {
                logger.error("failed to execute IMAP idle {serverName='" + connection.getServerName() + "',port='"
                        + connection.getPort() + "',userName='" + connection.getUsername() + "'}", me);
                break;
            } catch (Throwable t) {
                logger.error("hard error occurred while executing IMAP idle {serverName='"
                        + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                        + connection.getUsername() + "'}", t);
                break;
            }
        }
        logger.debug("imap idling loop exit {shutdown='" + shutdown + "'}");
    }

    public int archiveMessages(Message[] messages) {
        if (messages == null)
            return 0;
        logger.debug("archivemesages called()");

        if (archivePool.isShutdown()) {
            logger.debug("iap archive pool is shutdown. recreating new fixed thread pool {archiveThreads='"
                    + Config.getConfig().getArchiver().getArchiveThreads() + "'}");
            archivePool = Executors.newFixedThreadPool(Config.getConfig().getArchiver().getArchiveThreads());
        }
        int i = 0;
        logger.debug("iap executing archive thread pool jobs {messages='" + messages.length + "'}");
        for (Message message : messages) {
            logger.debug("adding message to thread pool {i='" + i + "'}");
            archivePool.execute(new MessageStoreAction(message));
            i++;
            if (i >= MAX_MESSAGES_TO_PROCESS) {
                logger.debug("iap max messages to process exceeded");
                break;
            }
        }
        shutdownAndAwaitTermination(archivePool, "archive workers");

        if (Compare.equalsIgnoreCase(connection.getProtocol().toString(), "IMAP") && messages != null
                && messages.length > 0) {
            IMAPFolder f = (IMAPFolder) inboxFolder;
            try {
                logger.debug("expunging deleted emails on IMAP server {serverName='" + connection.getServerName()
                        + "',port='" + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
                f.expunge();
                logger.debug("deleted emails have been expunged {serverName='" + connection.getServerName()
                        + "',port='" + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
            } catch (FolderNotFoundException folder) {
                logger.error("mail server inbox folder does not exist {serverName='" + connection.getServerName()
                        + "',port='" + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
                testOutput("mail server inbox folder does not exist {serverName='" + connection.getServerName()
                        + "',port='" + connection.getPort() + "',userName='" + connection.getUsername() + "'}");
            } catch (MessagingException me) {
                logger.error("messaging exception occurs while attempting to expunge IMAP folder {serverName='"
                        + connection.getServerName() + "',port='" + connection.getPort() + "',userName='"
                        + connection.getUsername() + "'}");
            } catch (Throwable e) {
                logger.error("error occurred while attempting to expunge IMAP folder:" + e.getMessage()
                        + " {serverName='" + connection.getServerName() + "',port='" + connection.getPort()
                        + "',userName='" + connection.getUsername() + "'}");
            }
        }
        logger.debug("archivemessages exited()");
        logger.debug("thread pool jobs complete");
        return i;
    }

    public void shutdownAndAwaitTermination(ExecutorService pool, String job) {
        pool.shutdown();
        try {
            logger.debug("awaiting termination of " + job);
            if (!pool.awaitTermination(DEAD_PERIOD, TimeUnit.MILLISECONDS)) {
                logger.debug("awaiting " + job + " did not terminate");
                pool.shutdownNow();
                if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                    logger.debug("awaiting " + job + " still did not terminate");
            } else {
                logger.debug("awaiting " + job + " terminated");
            }
        } catch (InterruptedException ie) {
            logger.debug("awaiting " + job + " were interrupted. shutting thread pool down immediately.");
            pool.shutdownNow();
            Thread.currentThread().interrupt();
        } catch (Throwable e) {
            logger.debug("awaiting " + job + " were interrupted:" + e.getMessage());
            pool.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    public void prepareShutdown() {
        logger.debug("iaprunnable prepare shutdown()");
        shutdown = true;
    }

    public void shutdown() {
        logger.debug("iaprunnable shutdown called()");
        try {
            disconnect();
        } catch (Throwable e) {
        }
        shutdown = true;
    }

    public void testOutput(String output) {
        if (testCallback != null)
            testCallback.statusUpdate(output);
    }

    protected void finalize() throws Throwable {
        interrupt();
        shutdown();
    }

    public void disconnect() throws Exception {
        logger.debug("iap disconnect called()");
        // here we want to make sure we are not disconnecting until complete of archive worker jobs
        if (!archivePool.isTerminated()) {
            shutdownAndAwaitTermination(archivePool, "archive workers");
        }
        logger.debug("closing folder");
        try {
            if (inboxFolder != null) {
                if (Compare.equalsIgnoreCase(connection.getProtocol().toString(), "IMAP")) {
                    inboxFolder.close(false);
                } else {
                    inboxFolder.close(true);
                }
            }
        } catch (Throwable e) {
            logger.error("failed to close inbox folder:" + e.getMessage());
        }
        logger.debug("closing store");
        try {
            if (store != null) {
                store.close();
            }
        } catch (Throwable e) {
            logger.error("failed to close store:" + e.getMessage());

        }
        inboxFolder = null;
        store = null;
        logger.debug("iap disconnect ended()");
    }

    public interface IAPTestCallback {

        public void statusUpdate(String result);

    }

    public class MessageStoreAction extends Thread implements StopBlockTarget {

        Message message;

        public MessageStoreAction(Message message) {
            this.message = message;
        }

        public void run() {
            try {
                logger.debug("found new message in mailbox queue");

                logger.debug("callback.store() called");

                if (message == null) {
                    logger.debug("failed to store iap message. message is null.");
                    try {
                        message.setFlag(Flags.Flag.SEEN, false);
                    } catch (Exception e5) {
                    }
                    return;
                }

                if (callback == null) {
                    logger.debug("failed to store iap message. callback is null.");
                    try {
                        message.setFlag(Flags.Flag.SEEN, false);
                    } catch (Exception e5) {
                    }
                    return;
                }

                Config.getStopBlockFactory().detectBlock("iap client", Thread.currentThread(), this, IDLE_TIMEOUT);
                if (message instanceof IMAPMessage) {
                    IMAPMessage imapMessage = (IMAPMessage) message;
                    callback.store(new BufferedInputStream(((IMAPMessage) imapMessage).getRFC822Stream()),
                            ipAddress);
                } else {
                    PipedOutputStream out = null;
                    PipedInputStream in = new PipedInputStream();
                    try {
                        out = new PipedOutputStream(in);
                    } catch (IOException ioe) {
                        logger.error("io exception occurred while creating piped output stream:" + ioe.getMessage(),
                                ioe);
                        return;
                    } catch (Throwable t) {
                        logger.error("exception occurred while creating piped output stream:" + t.getMessage(), t);
                        Config.getStopBlockFactory().endDetectBlock(Thread.currentThread());
                        return;
                    }
                    WriteMessageToOutputStream wmtos = new WriteMessageToOutputStream(message,
                            new BufferedOutputStream(out));
                    ExecutorService writeout = Executors.newSingleThreadExecutor();
                    writeout.execute(wmtos);
                    callback.store(in, ipAddress);
                }
                logger.debug("callback store complete");

                logger.debug("set deleted flag");
                try {
                    message.setFlag(Flags.Flag.DELETED, true);
                } catch (Exception e) {
                    try {
                        logger.error(
                                "failed to set deleted flag on message {subject='" + message.getSubject() + "'}",
                                e);
                    } catch (Throwable e2) {
                        logger.error("failed to retrieve subject from message (2):" + e.getMessage(), e);
                    }
                }
                logger.debug("message deleted");

            } catch (ArchiveException e) {
                try {
                    logger.error("failed to store message {subject='" + message.getSubject() + "'}", e);
                } catch (Throwable e2) {
                    logger.error("failed to retrieve subject from message (3):" + e.getMessage(), e);
                }
                if (e.getRecoveryDirective() == ArchiveException.RecoveryDirective.REJECT) {
                    try {
                        message.setFlag(Flags.Flag.SEEN, true);
                    } catch (Exception e5) {
                    }
                } else if (e.getRecoveryDirective() == ArchiveException.RecoveryDirective.RETRYLATER) {
                }
            } catch (Throwable t) {
                try {
                    logger.error("failed to store message {subject='" + message.getSubject() + "'}", t);
                } catch (Exception e2) {
                }
                try {
                    try {
                        message.setFlag(Flags.Flag.SEEN, true);
                    } catch (Exception e5) {
                    }
                } catch (Throwable e) {
                    try {
                        logger.error("failed to set seen flag on message {subject='" + message.getSubject() + "'}",
                                e);
                    } catch (MessagingException e2) {
                    }
                    return;
                }
            } finally {
                Config.getStopBlockFactory().endDetectBlock(Thread.currentThread());
            }

        }

        public void handleBlock(Thread thread) {
            if (store != null) {
                try {
                    logger.debug("close socket()");
                    store.close();
                } catch (Throwable t) {

                }
            }

            synchronized (this) {
                if (thread != null) {
                    logger.debug("interrupt()");
                    thread.interrupt();
                }
            }
        }

    }

    public class WriteMessageToOutputStream extends Thread {

        Message message;
        OutputStream os;

        public WriteMessageToOutputStream(Message message, OutputStream os) {
            this.message = message;
            this.os = os;
            setDaemon(true);
            setName("outputstreamwriter");
            setPriority(Thread.NORM_PRIORITY);
            logger.debug("writemessagetooutputstream contructor called");
        }

        public void run() {
            try {
                logger.debug("writeto() begin()");
                message.writeTo(os);
                os.flush();
                os.close();
                logger.debug("writeto() end()");
            } catch (Exception io) {
                logger.error("failed to write message to output stream:" + io.getMessage(), io);
                try {
                    os.close();
                } catch (Exception e) {
                }
            } catch (Throwable t) {
                logger.error("failed to write message to output stream:" + t.getMessage(), t);
            }

        }
    }

}