/* * Licensed 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 com.ok2c.lightmtp.impl.agent; import java.io.IOException; import java.nio.channels.SelectionKey; import java.util.Iterator; import java.util.concurrent.TimeUnit; import org.apache.http.nio.reactor.IOEventDispatch; import org.apache.http.nio.reactor.IOReactor; import org.apache.http.nio.reactor.IOSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ok2c.lightmtp.agent.MailTransport; import com.ok2c.lightmtp.impl.protocol.ProtocolState; abstract class AbstractMailTransport implements MailTransport { protected final Logger log = LoggerFactory.getLogger(getClass()); private final IOSessionRegistry sessionRegistry; private final IOReactorThreadCallback reactorThreadCallback; private volatile IOReactorThread thread; public AbstractMailTransport( final IOSessionRegistryCallback sessionRegistryCallback, final IOReactorThreadCallback reactorThreadCallback) { super(); this.sessionRegistry = new IOSessionRegistry(sessionRegistryCallback); this.reactorThreadCallback = reactorThreadCallback; } protected abstract IOReactor getIOReactor(); protected IOSessionRegistry getSessionRegistry() { return this.sessionRegistry; } protected void start(final IOEventDispatch iodispatch) { this.log.debug("Start I/O reactor"); this.thread = new IOReactorThread(getIOReactor(), iodispatch, this.reactorThreadCallback); this.thread.start(); } @Override public Exception getException() { IOReactorThread t = this.thread; if (t != null) { return t.getException(); } else { return null; } } protected void closeActiveSessions( final int timeout, final TimeUnit unit) throws InterruptedException { this.log.debug("Terminate active sessions"); synchronized (this.sessionRegistry) { Iterator<IOSession> sessions = this.sessionRegistry.iterator(); while (sessions.hasNext()) { IOSession session = sessions.next(); session.setAttribute(ProtocolState.ATTRIB, ProtocolState.QUIT); session.setEvent(SelectionKey.OP_WRITE); } long deadline = System.currentTimeMillis() + unit.toMillis(timeout); long remaining = timeout; boolean empty = false; while (!empty) { this.sessionRegistry.wait(remaining); empty = this.sessionRegistry.isEmpty(); if (timeout > 0) { remaining = deadline - System.currentTimeMillis(); if (remaining <= 0) { break; } } } if (!empty && this.log.isWarnEnabled()) { this.log.warn("Failed to shut down active sessions within " + timeout + " " + unit); } } } @Override public void shutdown() throws IOException { try { closeActiveSessions(30, TimeUnit.SECONDS); } catch (InterruptedException ignore) { } forceShutdown(30000); } protected void forceShutdown(final long gracePeriod) throws IOException { this.log.debug("Shut down I/O reactor"); getIOReactor().shutdown(gracePeriod); IOReactorThread t = this.thread; try { if (t != null) { t.join(1000); } } catch (InterruptedException ignore) { } } @Override public void forceShutdown() { try { forceShutdown(1000); } catch (IOException ignore) { } } }