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.net.BindException;
24 import java.net.InetSocketAddress;
25 import java.net.ServerSocket;
26 import java.nio.channels.CancelledKeyException;
27 import java.nio.channels.ClosedChannelException;
28 import java.nio.channels.SelectionKey;
29 import java.nio.channels.Selector;
30 import java.nio.channels.ServerSocketChannel;
31 import java.nio.channels.SocketChannel;
34 * NNTP daemon using SelectableChannels.
35 * @author Christian Lins
38 public final class NNTPDaemon extends AbstractDaemon
41 public static final Object RegisterGate = new Object();
43 private static NNTPDaemon instance = null;
45 public static synchronized NNTPDaemon createInstance(int port)
49 instance = new NNTPDaemon(port);
54 throw new RuntimeException("NNTPDaemon.createInstance() called twice");
60 private NNTPDaemon(final int port)
62 Log.msg("Server listening on port " + port, false);
71 // Create a Selector that handles the SocketChannel multiplexing
72 final Selector readSelector = Selector.open();
73 final Selector writeSelector = Selector.open();
75 // Start working threads
76 final int workerThreads = Runtime.getRuntime().availableProcessors() * 4;
77 ConnectionWorker[] cworkers = new ConnectionWorker[workerThreads];
78 for(int n = 0; n < workerThreads; n++)
80 cworkers[n] = new ConnectionWorker();
84 ChannelWriter.getInstance().setSelector(writeSelector);
85 ChannelReader.getInstance().setSelector(readSelector);
86 ChannelWriter.getInstance().start();
87 ChannelReader.getInstance().start();
89 final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
90 serverSocketChannel.configureBlocking(true); // Set to blocking mode
92 // Configure ServerSocket; bind to socket...
93 final ServerSocket serverSocket = serverSocketChannel.socket();
94 serverSocket.bind(new InetSocketAddress(this.port));
98 SocketChannel socketChannel;
102 // As we set the server socket channel to blocking mode the accept()
103 // method will block.
104 socketChannel = serverSocketChannel.accept();
105 socketChannel.configureBlocking(false);
106 assert socketChannel.isConnected();
107 assert socketChannel.finishConnect();
109 catch(IOException ex)
111 // Under heavy load an IOException "Too many open files may
112 // be thrown. It most cases we should slow down the connection
113 // accepting, to give the worker threads some time to process work.
114 Log.msg("IOException while accepting connection: " + ex.getMessage(), false);
115 Log.msg("Connection accepting sleeping for seconds...", true);
116 Thread.sleep(5000); // 5 seconds
120 final NNTPConnection conn;
123 conn = new NNTPConnection(socketChannel);
124 Connections.getInstance().add(conn);
126 catch(IOException ex)
128 Log.msg(ex.getLocalizedMessage(), false);
129 socketChannel.close();
135 SelectionKey selKeyWrite =
136 registerSelector(writeSelector, socketChannel, SelectionKey.OP_WRITE);
137 registerSelector(readSelector, socketChannel, SelectionKey.OP_READ);
139 Log.msg("Connected: " + socketChannel.socket().getRemoteSocketAddress(), true);
141 // Set write selection key and send hello to client
142 conn.setWriteSelectionKey(selKeyWrite);
143 conn.println("200 " + Config.getInstance().get(Config.HOSTNAME, "localhost")
144 + " " + Main.VERSION + " news server ready - (posting ok).");
146 catch(CancelledKeyException cke)
148 Log.msg("CancelledKeyException " + cke.getMessage() + " was thrown: "
149 + socketChannel.socket(), false);
151 catch(ClosedChannelException cce)
153 Log.msg("ClosedChannelException " + cce.getMessage() + " was thrown: "
154 + socketChannel.socket(), false);
158 catch(BindException ex)
160 // Could not bind to socket; this is a fatal problem; so perform shutdown
161 ex.printStackTrace();
164 catch(IOException ex)
166 ex.printStackTrace();
170 ex.printStackTrace();
174 public static SelectionKey registerSelector(final Selector selector,
175 final SocketChannel channel, final int op)
176 throws CancelledKeyException, ClosedChannelException
178 // Register the selector at the channel, so that it will be notified
179 // on the socket's events
180 synchronized(RegisterGate)
182 // Wakeup the currently blocking reader/writer thread; we have locked
183 // the RegisterGate to prevent the awakened thread to block again
186 // Lock the selector to prevent the waiting worker threads going into
187 // selector.select() which would block the selector.
188 synchronized (selector)
190 return channel.register(selector, op, null);