3 * see AUTHORS for the list of contributors
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 package org.sonews.daemon;
21 import org.sonews.util.Log;
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.CancelledKeyException;
25 import java.nio.channels.SelectionKey;
26 import java.nio.channels.Selector;
27 import java.nio.channels.SocketChannel;
28 import java.util.Iterator;
31 * A Thread task that processes OP_WRITE events for SocketChannels.
32 * @author Christian Lins
35 class ChannelWriter extends AbstractDaemon
38 private static ChannelWriter instance = new ChannelWriter();
41 * @return Returns the active ChannelWriter instance.
43 public static ChannelWriter getInstance()
47 private Selector selector = null;
49 protected ChannelWriter()
54 * @return Selector associated with this instance.
56 public Selector getSelector()
62 * Sets the selector that is used by this ChannelWriter.
65 public void setSelector(final Selector selector)
67 this.selector = selector;
76 assert selector != null;
80 SelectionKey selKey = null;
81 SocketChannel socketChannel = null;
82 NNTPConnection connection = null;
84 // select() blocks until some SelectableChannels are ready for
85 // processing. There is no need to synchronize the selector as we
86 // have only one thread per selector.
87 selector.select(); // The return value of select can be ignored
89 // Get list of selection keys with pending OP_WRITE events.
90 // The keySET is not thread-safe whereas the keys itself are.
91 Iterator it = selector.selectedKeys().iterator();
93 while (it.hasNext()) {
94 // We remove the first event from the set and store it for
96 selKey = (SelectionKey) it.next();
97 socketChannel = (SocketChannel) selKey.channel();
98 connection = Connections.getInstance().get(socketChannel);
101 if (connection != null) {
108 if (selKey != null) {
110 // Process the selected key.
111 // As there is only one OP_WRITE key for a given channel, we need
112 // not to synchronize this processing to retain the order.
113 processSelectionKey(connection, socketChannel, selKey);
114 } catch (IOException ex) {
115 Log.get().warning("Error writing to channel: " + ex);
117 // Cancel write events for this channel
119 connection.shutdownInput();
120 connection.shutdownOutput();
124 // Eventually wait for a register operation
125 synchronized (NNTPDaemon.RegisterGate) { /* do nothing */ }
126 } catch (CancelledKeyException ex) {
127 Log.get().info("ChannelWriter.run(): " + ex);
128 } catch (Exception ex) {
129 ex.printStackTrace();
131 } // while(isRunning())
134 private void processSelectionKey(final NNTPConnection connection,
135 final SocketChannel socketChannel, final SelectionKey selKey)
136 throws InterruptedException, IOException
138 assert connection != null;
139 assert socketChannel != null;
140 assert selKey != null;
141 assert selKey.isWritable();
143 // SocketChannel is ready for writing
144 if (selKey.isValid()) {
145 // Lock the socket channel
146 synchronized (socketChannel) {
147 // Get next output buffer
148 ByteBuffer buf = connection.getOutputBuffer();
150 // Currently we have nothing to write, so we stop the writeable
151 // events until we have something to write to the socket channel
153 selKey.interestOps(0);
154 // Update activity timestamp to prevent too early disconnects
155 // on slow client connections
156 connection.setLastActivity(System.currentTimeMillis());
160 while (buf != null) // There is data to be send
162 // Write buffer to socket channel; this method does not block
163 if (socketChannel.write(buf) <= 0) {
164 // Perhaps there is data to be written, but the SocketChannel's
165 // buffer is full, so we stop writing to until the next event.
168 // Retrieve next buffer if available; method may return the same
169 // buffer instance if it still have some bytes remaining
170 buf = connection.getOutputBuffer();
175 Log.get().warning("Invalid OP_WRITE key: " + selKey);
177 if (socketChannel.socket().isClosed()) {
178 connection.shutdownInput();
179 connection.shutdownOutput();
180 socketChannel.close();
181 Log.get().info("Connection closed.");