/**
* Licensed to niosmtp developers ('niosmtp') under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Selene licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.normanmaurer.niosmtp.impl;

import java.io.InputStream;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;

import me.normanmaurer.niosmtp.SMTPClient;
import me.normanmaurer.niosmtp.SMTPClientConfig;
import me.normanmaurer.niosmtp.SMTPClientFuture;
import me.normanmaurer.niosmtp.SMTPCommand;
import me.normanmaurer.niosmtp.impl.internal.SMTPClientConstants;
import me.normanmaurer.niosmtp.impl.internal.SMTPClientFutureImpl;
import me.normanmaurer.niosmtp.impl.internal.SMTPClientHandler;
import me.normanmaurer.niosmtp.impl.internal.SMTPClientPipelineFactory;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.timeout.IdleStateHandler;
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.Timer;

/**
 * {@link SMTPClient} implementation which will create a new Connection for
 * every
 * {@link #deliver(InetSocketAddress, String, List, InputStream, SMTPClientConfig)}
 * call.
 * 
 * So no pooling is active
 * 
 * @author Norman Maurer
 * 
 */
public class UnpooledSMTPClient implements SMTPClient, SMTPClientConstants {

    protected final NioClientSocketChannelFactory socketFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
    private final Timer timer = new HashedWheelTimer();

    protected ChannelPipelineFactory createChannelPipelineFactory() {
        return new SMTPClientPipelineFactory();
    }
    
    /*
     * (non-Javadoc)
     * @see me.normanmaurer.niosmtp.SMTPClient#deliver(java.net.InetSocketAddress, java.lang.String, java.util.List, java.io.InputStream, me.normanmaurer.niosmtp.SMTPClientConfig)
     */
    public SMTPClientFuture deliver(InetSocketAddress host, final String mailFrom, final List<String> recipients, final InputStream msg, final SMTPClientConfig config) {
        final SMTPClientFutureImpl future = new SMTPClientFutureImpl();
        ClientBootstrap bootstrap = new ClientBootstrap(socketFactory);
        bootstrap.setOption("connectTimeoutMillis", config.getConnectionTimeout() * 1000);
        bootstrap.setPipelineFactory(createChannelPipelineFactory());
        InetSocketAddress local = config.getLocalAddress();
        bootstrap.connect(host, local).addListener(new ChannelFutureListener() {
            
            @Override
            public void operationComplete(ChannelFuture cf) throws Exception {
                ChannelHandlerContext context = cf.getChannel().getPipeline().getContext(SMTPClientHandler.class);
                Map<String, Object> attrs = new HashMap<String, Object>();
                attrs.put(FUTURE_KEY, future);
                attrs.put(MAIL_FROM_KEY, mailFrom);
                attrs.put(RECIPIENTS_KEY, new LinkedList<String>(recipients));
                attrs.put(MSG_KEY, msg);
                attrs.put(SMTP_CONFIG_KEY, config);
                if (config.usePipelining()) {
                    attrs.put(NEXT_COMMAND_KEY, SMTPCommand.EHLO);
                } else {
                    attrs.put(NEXT_COMMAND_KEY, SMTPCommand.HELO);
                }
                context.setAttachment(attrs);

                // Add the idle timeout handler
                cf.getChannel().getPipeline().addFirst("idleHandler", new IdleStateHandler(timer, 0, 0, config.getResponseTimeout()));

                // Set the channel so we can close it for cancel later
                future.setChannel(cf.getChannel());
            }
        });
        return future;
    }

    /**
     * Call this method to destroy the {@link SMTPClient} and release all resources
     */
    public void destroy() {
        socketFactory.releaseExternalResources();
        timer.stop();
    }
}

@@ -966,24 +966,69 @@
 a.util.Map;%0A
+import java.util.concurrent.ExecutorService;%0A
 import java.
@@ -2413,70 +2413,50 @@
 ory(
-Executors.newCachedThreadPool(), Executors.newCachedThreadPool
+createBossExecutor(), createWorkerExecutor
 ());
@@ -2627,24 +2627,24 @@
 eFactory();%0A
-
     %7D%0A    %0A 
@@ -2638,24 +2638,519 @@
 %0A    %7D%0A    %0A
+    /**%0A     * Create the %7B@link ExecutorService%7D which is used for the BOSS Threads. %0A     * %0A     * @return bossExecutor%0A     */%0A    protected ExecutorService createBossExecutor() %7B%0A        return Executors.newCachedThreadPool();%0A    %7D%0A    %0A    /**%0A     * Create the %7B@link ExecutorService%7D which is used for the WORKER Threads%0A     * %0A     * @return workerExecutor%0A     */%0A    protected ExecutorService createWorkerExecutor() %7B%0A        return Executors.newCachedThreadPool();%0A    %7D%0A    %0A    %0A
     /*%0A     

Allow to overwrite the used ExecutorServices's