1.1 --- a/src/org/sonews/daemon/NNTPConnection.java Sun Sep 11 15:05:04 2011 +0200
1.2 +++ b/src/org/sonews/daemon/NNTPConnection.java Sun Sep 11 17:01:19 2011 +0200
1.3 @@ -15,7 +15,6 @@
1.4 * You should have received a copy of the GNU General Public License
1.5 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.6 */
1.7 -
1.8 package org.sonews.daemon;
1.9
1.10 import java.io.IOException;
1.11 @@ -30,6 +29,7 @@
1.12 import java.util.Arrays;
1.13 import java.util.Timer;
1.14 import java.util.TimerTask;
1.15 +import java.util.logging.Level;
1.16 import org.sonews.daemon.command.Command;
1.17 import org.sonews.storage.Article;
1.18 import org.sonews.storage.Group;
1.19 @@ -43,8 +43,7 @@
1.20 * @author Christian Lins
1.21 * @since sonews/0.5.0
1.22 */
1.23 -public final class NNTPConnection
1.24 -{
1.25 +public final class NNTPConnection {
1.26
1.27 public static final String NEWLINE = "\r\n"; // RFC defines this as newline
1.28 public static final String MESSAGE_ID_PATTERN = "<[^>]+>";
1.29 @@ -62,8 +61,7 @@
1.30 private SelectionKey writeSelKey = null;
1.31
1.32 public NNTPConnection(final SocketChannel channel)
1.33 - throws IOException
1.34 - {
1.35 + throws IOException {
1.36 if (channel == null) {
1.37 throw new IllegalArgumentException("channel is null");
1.38 }
1.39 @@ -77,8 +75,7 @@
1.40 * safe and returns true of the read lock was successfully set. If the lock
1.41 * is still hold by another Thread the method returns false.
1.42 */
1.43 - boolean tryReadLock()
1.44 - {
1.45 + boolean tryReadLock() {
1.46 // As synchronizing simple types may cause deadlocks,
1.47 // we use a gate object.
1.48 synchronized (readLockGate) {
1.49 @@ -96,8 +93,7 @@
1.50 * @throws IllegalMonitorStateException if a Thread not holding the lock
1.51 * tries to release it.
1.52 */
1.53 - void unlockReadLock()
1.54 - {
1.55 + void unlockReadLock() {
1.56 synchronized (readLockGate) {
1.57 if (readLock == Thread.currentThread().hashCode()) {
1.58 readLock = 0;
1.59 @@ -110,8 +106,7 @@
1.60 /**
1.61 * @return Current input buffer of this NNTPConnection instance.
1.62 */
1.63 - public ByteBuffer getInputBuffer()
1.64 - {
1.65 + public ByteBuffer getInputBuffer() {
1.66 return this.lineBuffers.getInputBuffer();
1.67 }
1.68
1.69 @@ -119,34 +114,29 @@
1.70 * @return Output buffer of this NNTPConnection which has at least one byte
1.71 * free storage.
1.72 */
1.73 - public ByteBuffer getOutputBuffer()
1.74 - {
1.75 + public ByteBuffer getOutputBuffer() {
1.76 return this.lineBuffers.getOutputBuffer();
1.77 }
1.78
1.79 /**
1.80 * @return ChannelLineBuffers instance associated with this NNTPConnection.
1.81 */
1.82 - public ChannelLineBuffers getBuffers()
1.83 - {
1.84 + public ChannelLineBuffers getBuffers() {
1.85 return this.lineBuffers;
1.86 }
1.87
1.88 /**
1.89 * @return true if this connection comes from a local remote address.
1.90 */
1.91 - public boolean isLocalConnection()
1.92 - {
1.93 + public boolean isLocalConnection() {
1.94 return ((InetSocketAddress) this.channel.socket().getRemoteSocketAddress()).getHostName().equalsIgnoreCase("localhost");
1.95 }
1.96
1.97 - void setWriteSelectionKey(SelectionKey selKey)
1.98 - {
1.99 + void setWriteSelectionKey(SelectionKey selKey) {
1.100 this.writeSelKey = selKey;
1.101 }
1.102
1.103 - public void shutdownInput()
1.104 - {
1.105 + public void shutdownInput() {
1.106 try {
1.107 // Closes the input line of the channel's socket, so no new data
1.108 // will be received and a timeout can be triggered.
1.109 @@ -156,14 +146,10 @@
1.110 }
1.111 }
1.112
1.113 - public void shutdownOutput()
1.114 - {
1.115 - cancelTimer.schedule(new TimerTask()
1.116 - {
1.117 -
1.118 + public void shutdownOutput() {
1.119 + cancelTimer.schedule(new TimerTask() {
1.120 @Override
1.121 - public void run()
1.122 - {
1.123 + public void run() {
1.124 try {
1.125 // Closes the output line of the channel's socket.
1.126 channel.socket().shutdownOutput();
1.127 @@ -178,41 +164,34 @@
1.128 }, 3000);
1.129 }
1.130
1.131 - public SocketChannel getSocketChannel()
1.132 - {
1.133 + public SocketChannel getSocketChannel() {
1.134 return this.channel;
1.135 }
1.136
1.137 - public Article getCurrentArticle()
1.138 - {
1.139 + public Article getCurrentArticle() {
1.140 return this.currentArticle;
1.141 }
1.142
1.143 - public Charset getCurrentCharset()
1.144 - {
1.145 + public Charset getCurrentCharset() {
1.146 return this.charset;
1.147 }
1.148
1.149 /**
1.150 * @return The currently selected communication channel (not SocketChannel)
1.151 */
1.152 - public Group getCurrentChannel()
1.153 - {
1.154 + public Group getCurrentChannel() {
1.155 return this.currentGroup;
1.156 }
1.157
1.158 - public void setCurrentArticle(final Article article)
1.159 - {
1.160 + public void setCurrentArticle(final Article article) {
1.161 this.currentArticle = article;
1.162 }
1.163
1.164 - public void setCurrentGroup(final Group group)
1.165 - {
1.166 + public void setCurrentGroup(final Group group) {
1.167 this.currentGroup = group;
1.168 }
1.169
1.170 - public long getLastActivity()
1.171 - {
1.172 + public long getLastActivity() {
1.173 return this.lastActivity;
1.174 }
1.175
1.176 @@ -222,8 +201,7 @@
1.177 * @throws IllegalArgumentException if raw is null.
1.178 * @throws IllegalStateException if calling thread does not own the readLock.
1.179 */
1.180 - void lineReceived(byte[] raw)
1.181 - {
1.182 + void lineReceived(byte[] raw) {
1.183 if (raw == null) {
1.184 throw new IllegalArgumentException("raw is null");
1.185 }
1.186 @@ -262,17 +240,25 @@
1.187 }
1.188 } catch (ClosedChannelException ex0) {
1.189 try {
1.190 - Log.get().info("Connection to " + channel.socket().getRemoteSocketAddress()
1.191 - + " closed: " + ex0);
1.192 + StringBuilder strBuf = new StringBuilder();
1.193 + strBuf.append("Connection to ");
1.194 + strBuf.append(channel.socket().getRemoteSocketAddress());
1.195 + strBuf.append(" closed: ");
1.196 + strBuf.append(ex0);
1.197 + Log.get().info(strBuf.toString());
1.198 } catch (Exception ex0a) {
1.199 ex0a.printStackTrace();
1.200 }
1.201 - } catch (Exception ex1) // This will catch a second StorageBackendException
1.202 - {
1.203 + } catch (Exception ex1) { // This will catch a second StorageBackendException
1.204 try {
1.205 command = null;
1.206 - ex1.printStackTrace();
1.207 - println("500 Internal server error");
1.208 + Log.get().log(Level.WARNING, ex1.getLocalizedMessage(), ex1);
1.209 + println("403 Internal server error");
1.210 +
1.211 + // Should we end the connection here?
1.212 + // RFC says we MUST return 400 before closing the connection
1.213 + shutdownInput();
1.214 + shutdownOutput();
1.215 } catch (Exception ex2) {
1.216 ex2.printStackTrace();
1.217 }
1.218 @@ -289,8 +275,7 @@
1.219 * @param line
1.220 * @return
1.221 */
1.222 - private Command parseCommandLine(String line)
1.223 - {
1.224 + private Command parseCommandLine(String line) {
1.225 String cmdStr = line.split(" ")[0];
1.226 return CommandSelector.getInstance().get(cmdStr);
1.227 }
1.228 @@ -303,8 +288,7 @@
1.229 * @param line
1.230 */
1.231 public void println(final CharSequence line, final Charset charset)
1.232 - throws IOException
1.233 - {
1.234 + throws IOException {
1.235 writeToChannel(CharBuffer.wrap(line), charset, line);
1.236 writeToChannel(CharBuffer.wrap(NEWLINE), charset, null);
1.237 }
1.238 @@ -315,8 +299,7 @@
1.239 * @param rawLines
1.240 */
1.241 public void println(final byte[] rawLines)
1.242 - throws IOException
1.243 - {
1.244 + throws IOException {
1.245 this.lineBuffers.addOutputBuffer(ByteBuffer.wrap(rawLines));
1.246 writeToChannel(CharBuffer.wrap(NEWLINE), charset, null);
1.247 }
1.248 @@ -328,9 +311,8 @@
1.249 * @throws java.io.IOException
1.250 */
1.251 private void writeToChannel(CharBuffer characters, final Charset charset,
1.252 - CharSequence debugLine)
1.253 - throws IOException
1.254 - {
1.255 + CharSequence debugLine)
1.256 + throws IOException {
1.257 if (!charset.canEncode()) {
1.258 Log.get().severe("FATAL: Charset " + charset + " cannot encode!");
1.259 return;
1.260 @@ -343,8 +325,7 @@
1.261 enableWriteEvents(debugLine);
1.262 }
1.263
1.264 - private void enableWriteEvents(CharSequence debugLine)
1.265 - {
1.266 + private void enableWriteEvents(CharSequence debugLine) {
1.267 // Enable OP_WRITE events so that the buffers are processed
1.268 try {
1.269 this.writeSelKey.interestOps(SelectionKey.OP_WRITE);
1.270 @@ -363,24 +344,20 @@
1.271 }
1.272
1.273 public void println(final CharSequence line)
1.274 - throws IOException
1.275 - {
1.276 + throws IOException {
1.277 println(line, charset);
1.278 }
1.279
1.280 public void print(final String line)
1.281 - throws IOException
1.282 - {
1.283 + throws IOException {
1.284 writeToChannel(CharBuffer.wrap(line), charset, line);
1.285 }
1.286
1.287 - public void setCurrentCharset(final Charset charset)
1.288 - {
1.289 + public void setCurrentCharset(final Charset charset) {
1.290 this.charset = charset;
1.291 }
1.292
1.293 - void setLastActivity(long timestamp)
1.294 - {
1.295 + void setLastActivity(long timestamp) {
1.296 this.lastActivity = timestamp;
1.297 }
1.298 }