# HG changeset patch
# User cli
# Date 1251117637 -7200
# Node ID 4b2c8bedb094c125997fa79cc4dc77f99ff87bbe
# Parent  6ae5e4f8329b46094f3328f622d8a2618d5c93cc
Refactoring in CommandSelector to allow manually loading of plugins.

diff -r 6ae5e4f8329b -r 4b2c8bedb094 helpers/usage
--- a/helpers/usage	Mon Aug 24 13:00:05 2009 +0200
+++ b/helpers/usage	Mon Aug 24 14:40:37 2009 +0200
@@ -1,9 +1,16 @@
 java -jar sonews.jar [arguments]
         where arguments:
-    -c|-config         <path to config file> if custom config file preferred
-    -dumpjdbcdriver    Prints out a list of available JDBC drivers
-    -feed              Enables feed daemon for pulling news from peer servers
-    -h|-help           This output
-    -mlgw              Enables the Mailinglist Gateway poller
-    -p <port>          Forces sonews to listen on the specified port.
-    -v|-version        Prints out the version info an exits.
+   -c|-config <config file>    If custom config file preferred
+   -dumpjdbcdriver             Prints out a list of available JDBC drivers
+   -feed                       Enables feed daemon for pulling/pushing news
+                                from/to peer servers
+   -h|-help                    This output
+   -mlgw                       Enables the Mailinglist Gateway poller
+   -p <port>                   Forces sonews to listen on the specified port
+   -plugin <name|class|file>   Loads the given plugin.
+   -plugin-command <class>     Loads the given class on startup as NNTP command
+                                handler. Can be specified multiple times. The
+                                class must be found in the JVMs classpath.
+   -plugin-storage <class>     Loads the given StorageProvider class. The class
+                                must be found in the JVMs classpath.
+   -v|-version                 Prints out the version info an exits.
diff -r 6ae5e4f8329b -r 4b2c8bedb094 org/sonews/Main.java
--- a/org/sonews/Main.java	Mon Aug 24 13:00:05 2009 +0200
+++ b/org/sonews/Main.java	Mon Aug 24 14:40:37 2009 +0200
@@ -22,8 +22,10 @@
 import java.sql.DriverManager;
 import java.util.Enumeration;
 import java.util.Date;
+import java.util.logging.Level;
 import org.sonews.config.Config;
 import org.sonews.daemon.ChannelLineBuffers;
+import org.sonews.daemon.CommandSelector;
 import org.sonews.daemon.Connections;
 import org.sonews.daemon.NNTPDaemon;
 import org.sonews.feed.FeedManager;
@@ -100,6 +102,26 @@
       {
         port = Integer.parseInt(args[++n]);
       }
+      else if(args[n].equals("-plugin"))
+      {
+        System.out.println("Warning: -plugin-storage is not implemented!");
+      }
+      else if(args[n].equals("-plugin-command"))
+      {
+        try
+        {
+          CommandSelector.addCommandHandler(args[++n]);
+        }
+        catch(Exception ex)
+        {
+          Log.get().warning("Could not load command plugin: " + args[n]);
+          Log.get().log(Level.INFO, "Main.java", ex);
+        }
+      }
+      else if(args[n].equals("-plugin-storage"))
+      {
+        System.out.println("Warning: -plugin-storage is not implemented!");
+      }
       else if(args[n].equals("-v") || args[n].equals("-version"))
       {
         // Simply return as the version info is already printed above
diff -r 6ae5e4f8329b -r 4b2c8bedb094 org/sonews/acl/AuthInfoCommand.java
--- a/org/sonews/acl/AuthInfoCommand.java	Mon Aug 24 13:00:05 2009 +0200
+++ b/org/sonews/acl/AuthInfoCommand.java	Mon Aug 24 14:40:37 2009 +0200
@@ -18,12 +18,47 @@
 
 package org.sonews.acl;
 
+import java.io.IOException;
+import org.sonews.daemon.NNTPConnection;
+import org.sonews.daemon.command.Command;
+import org.sonews.storage.StorageBackendException;
+
 /**
  *
  * @author Christian Lins
  * @since sonews/1.1
  */
-public class AuthInfoCommand
+public class AuthInfoCommand implements Command
 {
 
+  @Override
+  public String[] getSupportedCommandStrings()
+  {
+    throw new UnsupportedOperationException("Not supported yet.");
+  }
+
+  @Override
+  public boolean hasFinished()
+  {
+    throw new UnsupportedOperationException("Not supported yet.");
+  }
+
+  @Override
+  public String impliedCapability()
+  {
+    throw new UnsupportedOperationException("Not supported yet.");
+  }
+
+  @Override
+  public boolean isStateful()
+  {
+    throw new UnsupportedOperationException("Not supported yet.");
+  }
+
+  @Override
+  public void processLine(NNTPConnection conn, String line, byte[] rawLine) throws IOException, StorageBackendException
+  {
+    throw new UnsupportedOperationException("Not supported yet.");
+  }
+
 }
diff -r 6ae5e4f8329b -r 4b2c8bedb094 org/sonews/daemon/CommandSelector.java
--- a/org/sonews/daemon/CommandSelector.java	Mon Aug 24 13:00:05 2009 +0200
+++ b/org/sonews/daemon/CommandSelector.java	Mon Aug 24 14:40:37 2009 +0200
@@ -29,28 +29,17 @@
 /**
  * Selects the correct command processing class.
  * @author Christian Lins
+ * @since sonews/1.0
  */
-class CommandSelector
+public class CommandSelector
 {
 
   private static Map<Thread, CommandSelector> instances
     = new ConcurrentHashMap<Thread, CommandSelector>();
-  
-  public static CommandSelector getInstance()
-  {
-    CommandSelector csel = instances.get(Thread.currentThread());
-    if(csel == null)
-    {
-      csel = new CommandSelector();
-      instances.put(Thread.currentThread(), csel);
-    }
-    return csel;
-  }
+  private static Map<String, Class<?>> commandClassesMapping
+    = new ConcurrentHashMap<String, Class<?>>();
 
-  private Map<String, Command> commandMapping = new HashMap<String, Command>();
-  private Command              unsupportedCmd = new UnsupportedCommand();
-
-  private CommandSelector()
+  static
   {
     String[] classes = Resource.getAsString("helpers/commands.list", true).split("\n");
     for(String className : classes)
@@ -63,13 +52,7 @@
 
       try
       {
-        Class<?> clazz   = Class.forName(className);
-        Command  cmd     = (Command)clazz.newInstance();
-        String[] cmdStrs = cmd.getSupportedCommandStrings();
-        for(String cmdStr : cmdStrs)
-        {
-          this.commandMapping.put(cmdStr, cmd);
-        }
+        addCommandHandler(className);
       }
       catch(ClassNotFoundException ex)
       {
@@ -86,6 +69,35 @@
     }
   }
 
+  public static void addCommandHandler(String className)
+    throws ClassNotFoundException, InstantiationException, IllegalAccessException
+  {
+    Class<?> clazz = Class.forName(className);
+    Command cmd = (Command)clazz.newInstance();
+    String[] cmdStrs = cmd.getSupportedCommandStrings();
+    for (String cmdStr : cmdStrs)
+    {
+      commandClassesMapping.put(cmdStr, clazz);
+    }
+  }
+
+  public static CommandSelector getInstance()
+  {
+    CommandSelector csel = instances.get(Thread.currentThread());
+    if(csel == null)
+    {
+      csel = new CommandSelector();
+      instances.put(Thread.currentThread(), csel);
+    }
+    return csel;
+  }
+
+  private Map<String, Command> commandMapping = new HashMap<String, Command>();
+  private Command              unsupportedCmd = new UnsupportedCommand();
+
+  private CommandSelector()
+  {}
+
   public Command get(String commandName)
   {
     try
@@ -95,16 +107,23 @@
 
       if(cmd == null)
       {
-        return this.unsupportedCmd;
+        Class<?> clazz = commandClassesMapping.get(commandName);
+        if(clazz == null)
+        {
+          cmd = this.unsupportedCmd;
+        }
+        else
+        {
+          cmd = (Command)clazz.newInstance();
+          this.commandMapping.put(commandName, cmd);
+        }
       }
       else if(cmd.isStateful())
       {
-        return cmd.getClass().newInstance();
+        cmd = cmd.getClass().newInstance();
       }
-      else
-      {
-        return cmd;
-      }
+
+      return cmd;
     }
     catch(Exception ex)
     {
diff -r 6ae5e4f8329b -r 4b2c8bedb094 org/sonews/daemon/command/XDaemonCommand.java
--- a/org/sonews/daemon/command/XDaemonCommand.java	Mon Aug 24 13:00:05 2009 +0200
+++ b/org/sonews/daemon/command/XDaemonCommand.java	Mon Aug 24 14:40:37 2009 +0200
@@ -237,6 +237,10 @@
           conn.println("401 unknown sub command");
         }
       }
+      else if(commands.length >= 3 && commands[1].equalsIgnoreCase("PLUGIN"))
+      {
+        
+      }
       else
       {
         conn.println("400 invalid command usage");
diff -r 6ae5e4f8329b -r 4b2c8bedb094 org/sonews/plugin/Plugin.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org/sonews/plugin/Plugin.java	Mon Aug 24 14:40:37 2009 +0200
@@ -0,0 +1,42 @@
+/*
+ *   SONEWS News Server
+ *   see AUTHORS for the list of contributors
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sonews.plugin;
+
+/**
+ * A generic Plugin for sonews. Implementing classes do not really add new
+ * functionality to sonews but can use this interface as convenient procedure
+ * for installing functionality plugins, e.g. Command-Plugins or Storage-Plugins.
+ * @author Christian Lins
+ * @since sonews/1.1
+ */
+public interface Plugin
+{
+
+  /**
+   * Called when the Plugin is loaded by sonews. This method can be used
+   * by implementing classes to install additional or required plugins.
+   */
+  void load();
+
+  /**
+   * Called when the Plugin is unloaded by sonews.
+   */
+  void unload();
+
+}