Fix for last commit.
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()
48 private Selector selector = null;
50 protected ChannelWriter()
55 * @return Selector associated with this instance.
57 public Selector getSelector()
63 * Sets the selector that is used by this ChannelWriter.
66 public void setSelector(final Selector selector)
68 this.selector = selector;
77 assert selector != null;
83 SelectionKey selKey = null;
84 SocketChannel socketChannel = null;
85 NNTPConnection connection = null;
87 // select() blocks until some SelectableChannels are ready for
88 // processing. There is no need to synchronize the selector as we
89 // have only one thread per selector.
90 selector.select(); // The return value of select can be ignored
92 // Get list of selection keys with pending OP_WRITE events.
93 // The keySET is not thread-safe whereas the keys itself are.
94 Iterator it = selector.selectedKeys().iterator();
98 // We remove the first event from the set and store it for
100 selKey = (SelectionKey) it.next();
101 socketChannel = (SocketChannel) selKey.channel();
102 connection = Connections.getInstance().get(socketChannel);
105 if (connection != null)
119 // Process the selected key.
120 // As there is only one OP_WRITE key for a given channel, we need
121 // not to synchronize this processing to retain the order.
122 processSelectionKey(connection, socketChannel, selKey);
124 catch (IOException ex)
126 Log.get().warning("Error writing to channel: " + ex);
128 // Cancel write events for this channel
130 connection.shutdownInput();
131 connection.shutdownOutput();
135 // Eventually wait for a register operation
136 synchronized(NNTPDaemon.RegisterGate) { /* do nothing */ }
138 catch(CancelledKeyException ex)
140 Log.get().info("ChannelWriter.run(): " + ex);
144 ex.printStackTrace();
146 } // while(isRunning())
149 private void processSelectionKey(final NNTPConnection connection,
150 final SocketChannel socketChannel, final SelectionKey selKey)
151 throws InterruptedException, IOException
153 assert connection != null;
154 assert socketChannel != null;
155 assert selKey != null;
156 assert selKey.isWritable();
158 // SocketChannel is ready for writing
161 // Lock the socket channel
162 synchronized(socketChannel)
164 // Get next output buffer
165 ByteBuffer buf = connection.getOutputBuffer();
168 // Currently we have nothing to write, so we stop the writeable
169 // events until we have something to write to the socket channel
171 selKey.interestOps(0);
175 while(buf != null) // There is data to be send
177 // Write buffer to socket channel; this method does not block
178 if(socketChannel.write(buf) <= 0)
180 // Perhaps there is data to be written, but the SocketChannel's
181 // buffer is full, so we stop writing to until the next event.
186 // Retrieve next buffer if available; method may return the same
187 // buffer instance if it still have some bytes remaining
188 buf = connection.getOutputBuffer();
195 Log.get().warning("Invalid OP_WRITE key: " + selKey);
197 if(socketChannel.socket().isClosed())
199 connection.shutdownInput();
200 connection.shutdownOutput();
201 socketChannel.close();
202 Log.get().info("Connection closed.");