1.1 --- a/org/sonews/daemon/NNTPConnection.java Wed Jul 01 10:48:22 2009 +0200
1.2 +++ b/org/sonews/daemon/NNTPConnection.java Wed Jul 22 14:04:05 2009 +0200
1.3 @@ -21,33 +21,19 @@
1.4 import org.sonews.util.Log;
1.5 import java.io.IOException;
1.6 import java.net.InetSocketAddress;
1.7 +import java.net.SocketException;
1.8 import java.nio.ByteBuffer;
1.9 import java.nio.CharBuffer;
1.10 import java.nio.channels.ClosedChannelException;
1.11 import java.nio.channels.SelectionKey;
1.12 import java.nio.channels.SocketChannel;
1.13 import java.nio.charset.Charset;
1.14 +import java.util.Arrays;
1.15 import java.util.Timer;
1.16 import java.util.TimerTask;
1.17 -import org.sonews.daemon.command.ArticleCommand;
1.18 -import org.sonews.daemon.command.CapabilitiesCommand;
1.19 -import org.sonews.daemon.command.AbstractCommand;
1.20 -import org.sonews.daemon.command.GroupCommand;
1.21 -import org.sonews.daemon.command.HelpCommand;
1.22 -import org.sonews.daemon.command.ListCommand;
1.23 -import org.sonews.daemon.command.ListGroupCommand;
1.24 -import org.sonews.daemon.command.ModeReaderCommand;
1.25 -import org.sonews.daemon.command.NewGroupsCommand;
1.26 -import org.sonews.daemon.command.NextPrevCommand;
1.27 -import org.sonews.daemon.command.OverCommand;
1.28 -import org.sonews.daemon.command.PostCommand;
1.29 -import org.sonews.daemon.command.QuitCommand;
1.30 -import org.sonews.daemon.command.StatCommand;
1.31 -import org.sonews.daemon.command.UnsupportedCommand;
1.32 -import org.sonews.daemon.command.XDaemonCommand;
1.33 -import org.sonews.daemon.command.XPatCommand;
1.34 -import org.sonews.daemon.storage.Article;
1.35 -import org.sonews.daemon.storage.Group;
1.36 +import org.sonews.daemon.command.Command;
1.37 +import org.sonews.storage.Article;
1.38 +import org.sonews.storage.Channel;
1.39 import org.sonews.util.Stats;
1.40
1.41 /**
1.42 @@ -67,9 +53,9 @@
1.43 /** SocketChannel is generally thread-safe */
1.44 private SocketChannel channel = null;
1.45 private Charset charset = Charset.forName("UTF-8");
1.46 - private AbstractCommand command = null;
1.47 + private Command command = null;
1.48 private Article currentArticle = null;
1.49 - private Group currentGroup = null;
1.50 + private Channel currentGroup = null;
1.51 private volatile long lastActivity = System.currentTimeMillis();
1.52 private ChannelLineBuffers lineBuffers = new ChannelLineBuffers();
1.53 private int readLock = 0;
1.54 @@ -201,6 +187,11 @@
1.55 channel.socket().shutdownOutput();
1.56 channel.close();
1.57 }
1.58 + catch(SocketException ex)
1.59 + {
1.60 + // Socket was already disconnected
1.61 + Log.msg("NNTPConnection.shutdownOutput(): " + ex, true);
1.62 + }
1.63 catch(Exception ex)
1.64 {
1.65 Log.msg("NNTPConnection.shutdownOutput(): " + ex, false);
1.66 @@ -213,7 +204,7 @@
1.67 }, 3000);
1.68 }
1.69
1.70 - public SocketChannel getChannel()
1.71 + public SocketChannel getSocketChannel()
1.72 {
1.73 return this.channel;
1.74 }
1.75 @@ -227,8 +218,11 @@
1.76 {
1.77 return this.charset;
1.78 }
1.79 -
1.80 - public Group getCurrentGroup()
1.81 +
1.82 + /**
1.83 + * @return The currently selected communication channel (not SocketChannel)
1.84 + */
1.85 + public Channel getCurrentChannel()
1.86 {
1.87 return this.currentGroup;
1.88 }
1.89 @@ -238,7 +232,7 @@
1.90 this.currentArticle = article;
1.91 }
1.92
1.93 - public void setCurrentGroup(final Group group)
1.94 + public void setCurrentGroup(final Channel group)
1.95 {
1.96 this.currentGroup = group;
1.97 }
1.98 @@ -275,6 +269,7 @@
1.99 if(line.endsWith("\r"))
1.100 {
1.101 line = line.substring(0, line.length() - 1);
1.102 + raw = Arrays.copyOf(raw, raw.length - 1);
1.103 }
1.104
1.105 Log.msg("<< " + line, true);
1.106 @@ -288,7 +283,7 @@
1.107 try
1.108 {
1.109 // The command object will process the line we just received
1.110 - command.processLine(line);
1.111 + command.processLine(this, line, raw);
1.112 }
1.113 catch(ClosedChannelException ex0)
1.114 {
1.115 @@ -324,86 +319,14 @@
1.116 }
1.117
1.118 /**
1.119 - * This method performes several if/elseif constructs to determine the
1.120 - * fitting command object.
1.121 - * TODO: This string comparisons are probably slow!
1.122 + * This method determines the fitting command processing class.
1.123 * @param line
1.124 * @return
1.125 */
1.126 - private AbstractCommand parseCommandLine(String line)
1.127 + private Command parseCommandLine(String line)
1.128 {
1.129 - AbstractCommand cmd = new UnsupportedCommand(this);
1.130 - String cmdStr = line.split(" ")[0];
1.131 -
1.132 - if(cmdStr.equalsIgnoreCase("ARTICLE") ||
1.133 - cmdStr.equalsIgnoreCase("BODY"))
1.134 - {
1.135 - cmd = new ArticleCommand(this);
1.136 - }
1.137 - else if(cmdStr.equalsIgnoreCase("CAPABILITIES"))
1.138 - {
1.139 - cmd = new CapabilitiesCommand(this);
1.140 - }
1.141 - else if(cmdStr.equalsIgnoreCase("GROUP"))
1.142 - {
1.143 - cmd = new GroupCommand(this);
1.144 - }
1.145 - else if(cmdStr.equalsIgnoreCase("HEAD"))
1.146 - {
1.147 - cmd = new ArticleCommand(this);
1.148 - }
1.149 - else if(cmdStr.equalsIgnoreCase("HELP"))
1.150 - {
1.151 - cmd = new HelpCommand(this);
1.152 - }
1.153 - else if(cmdStr.equalsIgnoreCase("LIST"))
1.154 - {
1.155 - cmd = new ListCommand(this);
1.156 - }
1.157 - else if(cmdStr.equalsIgnoreCase("LISTGROUP"))
1.158 - {
1.159 - cmd = new ListGroupCommand(this);
1.160 - }
1.161 - else if(cmdStr.equalsIgnoreCase("MODE"))
1.162 - {
1.163 - cmd = new ModeReaderCommand(this);
1.164 - }
1.165 - else if(cmdStr.equalsIgnoreCase("NEWGROUPS"))
1.166 - {
1.167 - cmd = new NewGroupsCommand(this);
1.168 - }
1.169 - else if(cmdStr.equalsIgnoreCase("NEXT") ||
1.170 - cmdStr.equalsIgnoreCase("PREV"))
1.171 - {
1.172 - cmd = new NextPrevCommand(this);
1.173 - }
1.174 - else if(cmdStr.equalsIgnoreCase("OVER") ||
1.175 - cmdStr.equalsIgnoreCase("XOVER")) // for compatibility with older RFCs
1.176 - {
1.177 - cmd = new OverCommand(this);
1.178 - }
1.179 - else if(cmdStr.equalsIgnoreCase("POST"))
1.180 - {
1.181 - cmd = new PostCommand(this);
1.182 - }
1.183 - else if(cmdStr.equalsIgnoreCase("QUIT"))
1.184 - {
1.185 - cmd = new QuitCommand(this);
1.186 - }
1.187 - else if(cmdStr.equalsIgnoreCase("STAT"))
1.188 - {
1.189 - cmd = new StatCommand(this);
1.190 - }
1.191 - else if(cmdStr.equalsIgnoreCase("XDAEMON"))
1.192 - {
1.193 - cmd = new XDaemonCommand(this);
1.194 - }
1.195 - else if(cmdStr.equalsIgnoreCase("XPAT"))
1.196 - {
1.197 - cmd = new XPatCommand(this);
1.198 - }
1.199 -
1.200 - return cmd;
1.201 + String cmdStr = line.split(" ")[0];
1.202 + return CommandSelector.getInstance().get(cmdStr);
1.203 }
1.204
1.205 /**
1.206 @@ -419,6 +342,18 @@
1.207 writeToChannel(CharBuffer.wrap(line), charset, line);
1.208 writeToChannel(CharBuffer.wrap(NEWLINE), charset, null);
1.209 }
1.210 +
1.211 + /**
1.212 + * Writes the given raw lines to the output buffers and finishes with
1.213 + * a newline character (\r\n).
1.214 + * @param rawLines
1.215 + */
1.216 + public void println(final byte[] rawLines)
1.217 + throws IOException
1.218 + {
1.219 + this.lineBuffers.addOutputBuffer(ByteBuffer.wrap(rawLines));
1.220 + writeToChannel(CharBuffer.wrap(NEWLINE), charset, null);
1.221 + }
1.222
1.223 /**
1.224 * Encodes the given CharBuffer using the given Charset to a bunch of
1.225 @@ -440,6 +375,11 @@
1.226 LineEncoder lenc = new LineEncoder(characters, charset);
1.227 lenc.encode(lineBuffers);
1.228
1.229 + enableWriteEvents(debugLine);
1.230 + }
1.231 +
1.232 + private void enableWriteEvents(CharSequence debugLine)
1.233 + {
1.234 // Enable OP_WRITE events so that the buffers are processed
1.235 try
1.236 {