You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by sb...@apache.org on 2004/04/18 19:50:59 UTC

svn commit: rev 10085 - in james/jsieve/trunk: . src/java/org/apache/jsieve src/java/org/apache/jsieve/samples src/java/org/apache/jsieve/samples/james src/java/org/apache/jsieve/samples/james/junit src/java/org/apache/jsieve/samples/james/junit/utils

Author: sbrewin
Date: Sun Apr 18 10:50:58 2004
New Revision: 10085

Added:
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/ActionDispatcher.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/Actions.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/DestinationException.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/JSieve.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/SieveMailAdapter.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/ActionDispatcherTest.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/AllTests.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/utils/
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/utils/ActionAbsent.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/utils/MockMailetContext.java
   james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/package.html
Modified:
   james/jsieve/trunk/build.xml
   james/jsieve/trunk/default.properties
   james/jsieve/trunk/src/java/org/apache/jsieve/Arguments.java
   james/jsieve/trunk/src/java/org/apache/jsieve/Command.java
   james/jsieve/trunk/src/java/org/apache/jsieve/ConfigurationManager.java
   james/jsieve/trunk/src/java/org/apache/jsieve/Test.java
Log:
Added a samples package containing a Mailet for use in James.
This invokes jSieve to perform local delivery processing,
replacing the normal LocalDelivery Mailet.

NOT production quality. For demonstration purposes only!

Modified: james/jsieve/trunk/build.xml
==============================================================================
--- james/jsieve/trunk/build.xml	(original)
+++ james/jsieve/trunk/build.xml	Sun Apr 18 10:50:58 2004
@@ -181,6 +181,8 @@
         <classpath refid="project.class.path"/>
         <src path="${build.src}"/>
         <src path="${java.dir}"/>
+        <include name="org/apache/jsieve/**"/>
+        <exclude name="**/samples/*/**"/>
       </javac>
       <copy todir="${build.classes}">
         <fileset dir="${java.dir}">
@@ -202,7 +204,7 @@
 
       <delete dir="${build.javadocs}"/>
       <mkdir dir="${build.javadocs}"/>
-      <javadoc failonerror="yes" packagenames="org.apache.jsieve.*" destdir="${build.javadocs}"> 
+      <javadoc failonerror="yes" packagenames="org.apache.jsieve.*" excludepackagenames="org.apache.jsieve.samples.*" destdir="${build.javadocs}">
         <sourcepath>
             <pathelement path="${java.dir}"/>
             <pathelement path="${build.src}"/>            
@@ -260,7 +262,7 @@
     <target name="website" depends="xdocs,javadocs">
 
       <echo message="preparing website in ${www.dir}"/>
-      <delete>
+      <delete quiet="true">
         <fileset dir="${www.dir}">
           <exclude name="CVS/**"/>
           <exclude name="rfclist/**"/>
@@ -297,7 +299,7 @@
 
     <!--
     ===================================================================
-                                  Make jSieve jar
+                                  Make jSieve jars
     ===================================================================
     -->
 
@@ -310,14 +312,14 @@
       <jar jarfile="${build.lib}/${name}-${version}.jar" basedir="${build.classes}" manifest="${src.dir}/Manifest.mf">
           <include name="org/apache/jsieve/**"/>
           <exclude name="**/junit/*/**"/>
+          <exclude name="**/samples/*/**"/>
       </jar>
 
       <!-- Make jSieve jUnit jar-->
       <echo message="Making jSieve jUnit Jar (${name}-jUnit-${version}.jar)"/>
       <jar jarfile="${build.lib}/${name}-jUnit-${version}.jar" basedir="${build.classes}" manifest="${src.dir}/Manifest.mf">
-        <include name="org/apache/jsieve/junit/**"/>
+          <include name="org/apache/jsieve/junit/**"/>
       </jar>
-
     </target>
 
     <!--

Modified: james/jsieve/trunk/default.properties
==============================================================================
--- james/jsieve/trunk/default.properties	(original)
+++ james/jsieve/trunk/default.properties	Sun Apr 18 10:50:58 2004
@@ -7,7 +7,7 @@
 # See also: include.properties
 
 name=jsieve
-Name=jSieve Mail Server
+Name=jSieve Mail Filter
 version=0.1
 year=2004
 extension.name=org.apache.jsieve

Modified: james/jsieve/trunk/src/java/org/apache/jsieve/Arguments.java
==============================================================================
--- james/jsieve/trunk/src/java/org/apache/jsieve/Arguments.java	(original)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/Arguments.java	Sun Apr 18 10:50:58 2004
@@ -147,7 +147,7 @@
         return "Arguments: "
             + getArgumentList().toString()
             + " Tests: "
-            + getTestList().toString();
+            + (hasTests() ? getTestList().toString(): "null");
     }
 
 }

Modified: james/jsieve/trunk/src/java/org/apache/jsieve/Command.java
==============================================================================
--- james/jsieve/trunk/src/java/org/apache/jsieve/Command.java	(original)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/Command.java	Sun Apr 18 10:50:58 2004
@@ -34,7 +34,7 @@
     {
         Log log = Logger.getLog();
         if (log.isDebugEnabled())
-            log.debug("Command: " + getName());
+            log.debug(toString());
         return CommandManager.getInstance().newInstance(getName()).execute(
             mail,
             getArguments(),
@@ -98,9 +98,9 @@
     {
         return "Command name: "
             + getName()
-            + " Arguments: "
+            + " "
             + ((getArguments() == null) ? "null" : getArguments().toString())
-            + "Block: "
+            + " Block: "
             + ((getBlock() == null) ? "null" : getBlock().toString());
     }
 

Modified: james/jsieve/trunk/src/java/org/apache/jsieve/ConfigurationManager.java
==============================================================================
--- james/jsieve/trunk/src/java/org/apache/jsieve/ConfigurationManager.java	(original)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/ConfigurationManager.java	Sun Apr 18 10:50:58 2004
@@ -1,65 +1,58 @@
-/***********************************************************************
- * Copyright (c) 2003-2004 The Apache Software Foundation.             *
- * All rights reserved.                                                *
+/*******************************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation. * All rights
+ * reserved. *
  * ------------------------------------------------------------------- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you *
- * may not use this file except in compliance with the License. You    *
- * may obtain a copy of the License at:                                *
- *                                                                     *
- *     http://www.apache.org/licenses/LICENSE-2.0                      *
- *                                                                     *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you * may
+ * not use this file except in compliance with the License. You * may obtain a
+ * copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * *
  * Unless required by applicable law or agreed to in writing, software *
- * distributed under the License is distributed on an "AS IS" BASIS,   *
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
- * implied.  See the License for the specific language governing       *
- * permissions and limitations under the License.                      *
- ***********************************************************************/
-
+ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the
+ * License for the specific language governing * permissions and limitations
+ * under the License. *
+ ******************************************************************************/
 package org.apache.jsieve;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
-
 import org.apache.commons.digester.Digester;
 import org.apache.commons.logging.Log;
 import org.xml.sax.SAXException;
-
 /**
- * <p>Singleton class <code>ConfigurationManager</code> parses the XML statements in
- * the Sieve configuration file and translates them to Java objects.</p>
+ * <p>
+ * Singleton class <code>ConfigurationManager</code> parses the XML
+ * statements in the Sieve configuration file and translates them to Java
+ * objects.
+ * </p>
  * 
- * <p>The Sieve configuration file is named <code>sieveConfig.xml</code>. It is
- * located by searching the classpath of the current ClassLoader.</p>
+ * <p>
+ * The Sieve configuration file is named <code>sieveConfig.xml</code>. It is
+ * located by searching the classpath of the current ClassLoader.
+ * </p>
  */
 public class ConfigurationManager
 {
     /**
      * The sole instance of the receiver.
-     */ 
+     */
     static private ConfigurationManager fieldInstance;
-    
     /**
      * The Digester used to process the Sieve configuration XML.
-     */ 
+     */
     private Digester fieldDigester;
-    
     /**
      * A Map of the Command names and their associated class names.
-     */ 
+     */
     private Map fieldCommandMap;
-    
     /**
      * A Map of the Test names and their associated class names.
-     */ 
+     */
     private Map fieldTestMap;
-    
     /**
      * A Map of the Comparator names and their associated class names.
-     */ 
+     */
     private Map fieldComparatorMap;
-
     /**
      * Constructor for ConfigurationManager.
      * 
@@ -72,27 +65,26 @@
         try
         {
             parse();
-        }
-        catch (SAXException e)
+        } catch (SAXException e)
         {
             if (log.isErrorEnabled())
                 log.error("Exception processing Configuration: ", e);
             throw new SieveConfigurationException(e);
-        }
-        catch (IOException e)
+        } catch (IOException e)
         {
             if (log.isErrorEnabled())
                 log.error("Exception processing Configuration: ", e);
             throw new SieveConfigurationException(e);
         }
     }
-
     /**
-     * Returns the sole instance of the receiver, lazily initialised if required.
+     * Returns the sole instance of the receiver, lazily initialised if
+     * required.
+     * 
      * @return ConfigurationManager
      */
     static public synchronized ConfigurationManager getInstance()
-        throws SieveConfigurationException
+            throws SieveConfigurationException
     {
         ConfigurationManager instance = null;
         if (null == (instance = getInstanceBasic()))
@@ -102,35 +94,34 @@
         }
         return instance;
     }
-    
     /**
      * Returns the sole instance of the receiver.
+     * 
      * @return ConfigurationManager
      */
     static private ConfigurationManager getInstanceBasic()
     {
         return fieldInstance;
     }
-    
     /**
      * Returns a new instance of the receiver.
+     * 
      * @return ConfigurationManager
      */
     static protected ConfigurationManager computeInstance()
-        throws SieveConfigurationException
+            throws SieveConfigurationException
     {
         return new ConfigurationManager();
-    }        
-
+    }
     /**
      * Sets the instance.
+     * 
      * @param instance The instance to set
      */
     static protected void setInstance(ConfigurationManager instance)
     {
         fieldInstance = instance;
     }
-    
     /**
      * Updates the instance.
      */
@@ -138,27 +129,32 @@
     {
         setInstance(computeInstance());
     }
-    
     /**
      * Method getConfigName answers the name of the Sieve configuration file.
+     * 
      * @return String
      */
     static protected String getConfigName()
     {
         return "sieveConfig.xml";
-    }    
-
+    }
     /**
-     * Method getConfigStream answers an InputStream over the Sieve configuration 
-     * file. It is located by searching the classpath of the current ClassLoader.
+     * Method getConfigStream answers an InputStream over the Sieve
+     * configuration file. It is located by searching the classpath of the
+     * current ClassLoader.
      * 
      * @return InputStream
+     * @throws IOException
      */
-    static protected InputStream getConfigStream()
-    {    
-        return ClassLoader.getSystemResourceAsStream(getConfigName());
-    }    
-    
+    static protected InputStream getConfigStream() throws IOException
+    {
+        InputStream stream = ConfigurationManager.class.getClassLoader()
+                .getResourceAsStream(getConfigName());
+        if (null == stream)
+            throw new IOException("Resource \"" + getConfigName()
+                    + "\" not found");
+        return stream;
+    }
     /**
      * Method getCommandMap answers a Map of Command names and their associated
      * class names, lazily initialized if required.
@@ -172,13 +168,12 @@
         {
             updateCommandMap();
             return getCommandMap();
-        }    
+        }
         return commandMap;
     }
-    
     /**
-     * Method getTestMap answers a Map of Test names and their associated
-     * class names, lazily initialized if required.
+     * Method getTestMap answers a Map of Test names and their associated class
+     * names, lazily initialized if required.
      * 
      * @return Map
      */
@@ -189,13 +184,12 @@
         {
             updateTestMap();
             return getTestMap();
-        }    
+        }
         return testMap;
     }
-    
     /**
-     * Method getComparatorMap answers a Map of Comparator names and their associated
-     * class names, lazily initialized if required.
+     * Method getComparatorMap answers a Map of Comparator names and their
+     * associated class names, lazily initialized if required.
      * 
      * @return Map
      */
@@ -206,13 +200,12 @@
         {
             updateComparatorMap();
             return getComparatorMap();
-        }    
+        }
         return comparatorMap;
-    }        
-    
+    }
     /**
-     * Method getCommandMapBasic answers a Map of Command names and their associated
-     * class names.
+     * Method getCommandMapBasic answers a Map of Command names and their
+     * associated class names.
      * 
      * @return Map
      */
@@ -220,7 +213,6 @@
     {
         return fieldCommandMap;
     }
-    
     /**
      * Method getTestMapBasic answers a Map of Test names and their associated
      * class names.
@@ -231,48 +223,46 @@
     {
         return fieldTestMap;
     }
-    
     /**
-     * Method getComparatorMapBasic answers a Map of Comparator names and their a
-     * ssociated class names.
+     * Method getComparatorMapBasic answers a Map of Comparator names and their
+     * a ssociated class names.
      * 
      * @return Map
      */
     private Map getComparatorMapBasic()
     {
         return fieldComparatorMap;
-    }            
-    
+    }
     /**
      * Method computeCommandMap answers a new CommandMap.
+     * 
      * @return Map
      */
     protected Map computeCommandMap()
     {
         return new HashMap();
     }
-    
     /**
      * Method computeTestMap answers a new TestMap.
+     * 
      * @return Map
      */
     protected Map computeTestMap()
     {
         return new HashMap();
-    } 
-    
+    }
     /**
      * Method computeComparatorMap answers a new ComparatorMap.
+     * 
      * @return Map
      */
     protected Map computeComparatorMap()
     {
         return new HashMap();
-    }       
-    
+    }
     /**
-     * Method putCommandMapEntry adds an association between a Command name and an
-     * implementation class to the Command Map.
+     * Method putCommandMapEntry adds an association between a Command name and
+     * an implementation class to the Command Map.
      * 
      * @param name
      * @param className
@@ -281,7 +271,6 @@
     {
         getCommandMap().put(name, className);
     }
-    
     /**
      * Method putTestMapEntry adds an association between a Test name and an
      * implementation class to the Test Map.
@@ -293,10 +282,9 @@
     {
         getTestMap().put(name, className);
     }
-    
     /**
-     * Method putComparatorMapEntry adds an association between a Comparator name and
-     * an implementation class to the Comparator Map.
+     * Method putComparatorMapEntry adds an association between a Comparator
+     * name and an implementation class to the Comparator Map.
      * 
      * @param name
      * @param className
@@ -304,10 +292,10 @@
     public void putComparatorMapEntry(String name, String className)
     {
         getComparatorMap().put(name, className);
-    }                        
-
+    }
     /**
      * Returns the digester, lazily initialised if required.
+     * 
      * @return Digester
      */
     protected synchronized Digester getDigester()
@@ -316,23 +304,22 @@
         if (null == (digester = getDigesterBasic()))
         {
             updateDigester();
-            return getDigester();    
-        }    
+            return getDigester();
+        }
         return digester;
     }
-    
     /**
      * Returns the digester.
+     * 
      * @return Digester
      */
     private Digester getDigesterBasic()
     {
         return fieldDigester;
     }
-    
     /**
-     * Method computeDigester answers a new digester intialised with the rules to
-     * parse the Sieve configuration file.
+     * Method computeDigester answers a new digester intialised with the rules
+     * to parse the Sieve configuration file.
      * 
      * @return Digester
      */
@@ -341,46 +328,33 @@
         Digester digester = new Digester();
         digester.push(this);
         digester.setValidating(false);
-
         // CommandMap rules
-        digester.addCallMethod(
-            "sieve/commandMap/entry",
-            "putCommandMapEntry",
-            2,
-            new Class[] { String.class, String.class });
+        digester.addCallMethod("sieve/commandMap/entry", "putCommandMapEntry",
+                2, new Class[]{String.class, String.class});
         digester.addCallParam("sieve/commandMap/entry/name", 0);
         digester.addCallParam("sieve/commandMap/entry/class", 1);
-        
         // TestMap rules
-        digester.addCallMethod(
-            "sieve/testMap/entry",
-            "putTestMapEntry",
-            2,
-            new Class[] { String.class, String.class });
+        digester.addCallMethod("sieve/testMap/entry", "putTestMapEntry", 2,
+                new Class[]{String.class, String.class});
         digester.addCallParam("sieve/testMap/entry/name", 0);
         digester.addCallParam("sieve/testMap/entry/class", 1);
-        
         // ComparatorMap rules
-        digester.addCallMethod(
-            "sieve/comparatorMap/entry",
-            "putComparatorMapEntry",
-            2,
-            new Class[] { String.class, String.class });
+        digester.addCallMethod("sieve/comparatorMap/entry",
+                "putComparatorMapEntry", 2, new Class[]{String.class,
+                        String.class});
         digester.addCallParam("sieve/comparatorMap/entry/name", 0);
-        digester.addCallParam("sieve/comparatorMap/entry/class", 1);                  
-
+        digester.addCallParam("sieve/comparatorMap/entry/class", 1);
         return digester;
-    }       
-
+    }
     /**
      * Sets the digester.
+     * 
      * @param digester The digester to set
      */
     protected void setDigester(Digester digester)
     {
         fieldDigester = digester;
     }
-    
     /**
      * Updates the digester.
      */
@@ -388,9 +362,8 @@
     {
         setDigester(computeDigester());
     }
-    
     /**
-     * Method parse uses the Digester to parse the XML statements in the Sieve 
+     * Method parse uses the Digester to parse the XML statements in the Sieve
      * configuration file into Java objects.
      * 
      * @return Object
@@ -400,35 +373,34 @@
     protected Object parse() throws SAXException, IOException
     {
         return getDigester().parse(getConfigStream());
-    }        
-
+    }
     /**
      * Sets the commandMap.
+     * 
      * @param commandMap The commandMap to set
      */
     protected void setCommandMap(Map commandMap)
     {
         fieldCommandMap = commandMap;
     }
-    
     /**
      * Sets the testMap.
+     * 
      * @param testMap The testMap to set
      */
     protected void setTestMap(Map testMap)
     {
         fieldTestMap = testMap;
     }
-    
     /**
      * Sets the comparatorMap.
+     * 
      * @param comparatorMap The comparatorMap to set
      */
     protected void setComparatorMap(Map comparatorMap)
     {
         fieldComparatorMap = comparatorMap;
-    }        
-    
+    }
     /**
      * Updates the commandMap.
      */
@@ -436,7 +408,6 @@
     {
         setCommandMap(computeCommandMap());
     }
-    
     /**
      * Updates the testMap.
      */
@@ -444,13 +415,11 @@
     {
         setTestMap(computeTestMap());
     }
-    
     /**
      * Updates the comparatorMap.
      */
     protected void updateComparatorMap()
     {
         setComparatorMap(computeComparatorMap());
-    }            
-
+    }
 }

Modified: james/jsieve/trunk/src/java/org/apache/jsieve/Test.java
==============================================================================
--- james/jsieve/trunk/src/java/org/apache/jsieve/Test.java	(original)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/Test.java	Sun Apr 18 10:50:58 2004
@@ -41,7 +41,7 @@
     {
         Log log = Logger.getLog();
         if (log.isDebugEnabled())
-            log.debug("Test: " + getName());
+            log.debug(toString());
         return new Boolean(
             TestManager.getInstance().newInstance(getName()).execute(
                 mail,
@@ -111,7 +111,7 @@
     {
         return "Test name: "
             + getName()
-            + " Arguments: "
+            + " "
             + (getArguments() == null ? "null" : getArguments().toString());
     }
 

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/ActionDispatcher.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/ActionDispatcher.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,239 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.ActionFileInto;
+import org.apache.jsieve.mail.ActionKeep;
+import org.apache.jsieve.mail.ActionRedirect;
+import org.apache.jsieve.mail.ActionReject;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetContext;
+
+/**
+ * Singleton Class <code>ActionDispatcher</code> dynamically dispatches 
+ * an Action depending on the type of Action received at runtime. 
+ */
+public class ActionDispatcher
+{
+    /**
+     * The sole instance of the receiver. 
+     */
+    static private ActionDispatcher fieldInstance;
+    
+    /**
+     * A Map keyed by the type of Action. The values are the methods to invoke to 
+     * handle the Action.
+     */ 
+    private Map fieldMethodMap;
+
+    /**
+     * Constructor for ActionDispatcher.
+     */
+    private ActionDispatcher()
+    {
+        super();
+    }
+
+    /**
+     * Returns the sole instance of the receiver, lazily initialised.
+     * @return ActionDispatcher
+     */
+    public static synchronized ActionDispatcher getInstance()
+    {
+        ActionDispatcher instance = null;
+        if (null == (instance = getInstanceBasic()))
+        {
+            updateInstance();
+            return getInstance();
+        }    
+        return instance;
+    }
+    
+    /**
+     * Returns the sole instance of the receiver.
+     * @return ActionDispatcher
+     */
+    private static ActionDispatcher getInstanceBasic()
+    {
+        return fieldInstance;
+    }    
+    
+    /**
+     * Returns a new instance of the receiver.
+     * @return ActionDispatcher
+     */
+    protected static ActionDispatcher computeInstance()
+    {
+        return new ActionDispatcher();
+    }    
+
+    /**
+     * Sets the instance.
+     * @param instance The instance to set
+     */
+    protected static void setInstance(ActionDispatcher instance)
+    {
+        fieldInstance = instance;
+    }
+  
+    
+    /**
+     * Resets the instance.
+     */
+    public static void resetInstance()
+    {
+        setInstance(null);
+    }    
+    
+    /**
+     * Updates the instance.
+     */
+    protected static void updateInstance()
+    {
+        setInstance(computeInstance());
+    }
+    
+    /**
+     * Method execute executes the passed Action by invoking the method mapped by the
+     * receiver with a parameter of the EXACT type of Action.
+     * @param anAction
+     * @param aMail
+     * @param aMailetContext
+     * @throws NoSuchMethodException
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     * @throws MessagingException
+     */
+    public void execute(
+        Action anAction,
+        Mail aMail,
+        MailetContext aMailetContext)
+        throws
+            NoSuchMethodException,
+            IllegalAccessException,
+            InvocationTargetException,
+            MessagingException
+    {
+        Method actionMethod = (Method) getMethodMap().get(anAction.getClass());
+        if (null == actionMethod)
+            throw new NoSuchMethodException(
+                "Method accepting parameters ("
+                    + anAction.getClass().getName()
+                    + ", "
+                    + aMail.getClass().getName()
+                    + ", "
+                    + aMailetContext.getClass().getName()
+                    + ") not mapped.");
+        actionMethod.invoke(
+            null,
+            new Object[] { anAction, aMail, aMailetContext });
+    }
+    
+    /**
+     * Returns the methodMap, lazily initialised.
+     * @return Map
+     * @throws NoSuchMethodException
+     */
+    protected synchronized Map getMethodMap() throws NoSuchMethodException
+    {
+        Map methodMap = null;
+        if (null == (methodMap = getMethodMapBasic()))
+        {
+            updateMethodMap();
+            return getMethodMap();
+        }    
+        return methodMap;
+    }
+    
+    /**
+     * Returns the methodMap.
+     * @return Map
+     */
+    private Map getMethodMapBasic()
+    {
+        return fieldMethodMap;
+    }    
+    
+    /**
+     * Returns a new methodMap.
+     * @return Map
+     */
+    protected Map computeMethodMap() throws NoSuchMethodException
+    {
+        Map methodNameMap = new HashMap();
+        methodNameMap.put(
+            ActionFileInto.class,
+            Actions.class.getMethod(
+                "execute",
+                new Class[] {
+                    ActionFileInto.class,
+                    Mail.class,
+                    MailetContext.class }));
+        methodNameMap.put(
+            ActionKeep.class,
+            Actions.class.getMethod(
+                "execute",
+                new Class[] {
+                    ActionKeep.class,
+                    Mail.class,
+                    MailetContext.class }));
+        methodNameMap.put(
+            ActionRedirect.class,
+            Actions.class.getMethod(
+                "execute",
+                new Class[] {
+                    ActionRedirect.class,
+                    Mail.class,
+                    MailetContext.class }));
+        methodNameMap.put(
+            ActionReject.class,
+            Actions.class.getMethod(
+                "execute",
+                new Class[] {
+                    ActionReject.class,
+                    Mail.class,
+                    MailetContext.class }));
+        return methodNameMap;
+    }    
+
+    /**
+     * Sets the methodMap.
+     * @param methodMap The methodMap to set
+     */
+    protected void setMethodMap(Map methodMap)
+    {
+        fieldMethodMap = methodMap;
+    }
+    
+    /**
+     * Updates the methodMap.
+     * @throws NoSuchMethodException
+     */
+    protected void updateMethodMap() throws NoSuchMethodException
+    {
+        setMethodMap(computeMethodMap());
+    }
+
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/Actions.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/Actions.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,386 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.mail.Address;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.james.util.mail.mdn.ActionModeAutomatic;
+import org.apache.james.util.mail.mdn.Disposition;
+import org.apache.james.util.mail.mdn.DispositionModifier;
+import org.apache.james.util.mail.mdn.MDNFactory;
+import org.apache.james.util.mail.mdn.ModifierError;
+import org.apache.james.util.mail.mdn.SendingModeAutomatic;
+import org.apache.james.util.mail.mdn.TypeDeleted;
+import org.apache.jsieve.mail.ActionFileInto;
+import org.apache.jsieve.mail.ActionKeep;
+import org.apache.jsieve.mail.ActionRedirect;
+import org.apache.jsieve.mail.ActionReject;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+
+/**
+ * Singleton Class <code>Actions</code> implements <code>execute()</code>
+ * methods for each of the supported Actions.
+ */
+public class Actions
+{
+    static private String fieldAttributePrefix;
+
+    /**
+     * Constructor for Actions.
+     */
+    private Actions()
+    {
+        super();
+    }
+
+    /**
+     * <p>
+     * Executes the passed ActionFileInto.
+     * </p>
+     * 
+     * <p>
+     * This implementation accepts any destination with the root of <code>INBOX</code>.
+     * </p>
+     * 
+     * <p>
+     * As the current POP3 server does not support sub-folders, the mail is
+     * stored in the INBOX for the recipient of the mail and the full intended
+     * destination added as a prefix to the message's subject.
+     * </p>
+     * 
+     * <p>
+     * When IMAP support is added to James, it will be possible to support
+     * sub-folders of <code>INBOX</code> fully.
+     * </p>
+     * 
+     * @param anAction
+     * @param aMail
+     * @param aMailetContext
+     * @throws MessagingException
+     */
+    static public void execute(ActionFileInto anAction, Mail aMail,
+            MailetContext aMailetContext) throws MessagingException
+    {
+        StringBuffer repositoryDestinationBuffer = null;
+        MailAddress recipient = null;
+        boolean delivered = false;
+        try
+        {
+            recipient = getSoleRecipient(aMail);
+            // Validate and translate the destination
+            // Note that we do not check that the repository implementation
+            // supports the destination or that it exists. That is left
+            // to the repository implementation
+            int endOfRootDestination = anAction.getDestination().indexOf('/');
+            String rootDestination = (endOfRootDestination > -1
+                    ? anAction.getDestination().substring(0,
+                            endOfRootDestination)
+                    : anAction.getDestination());
+            if (!rootDestination.equalsIgnoreCase("INBOX"))
+                throw new DestinationException("Folder: \""
+                        + anAction.getDestination() + "\"");
+            repositoryDestinationBuffer = new StringBuffer(recipient.toString());
+            if (endOfRootDestination > -1)
+                repositoryDestinationBuffer.append(anAction.getDestination()
+                        .substring(endOfRootDestination).replace('/', '.'));
+            // Adapted from LocalDelivery Mailet
+            // Add qmail's de facto standard Delivered-To header
+            MimeMessage localMessage = new MimeMessage(aMail.getMessage())
+            {
+                protected void updateHeaders() throws MessagingException
+                {
+                    if (getMessageID() == null)
+                        super.updateHeaders();
+                    else
+                        modified = false;
+                }
+            };
+            localMessage.addHeader("Delivered-To", recipient.toString());
+            // For now, a prefix "[destination]" is added to the
+            // header so that we can see what the true destination
+            // would have been with support for sub-folders
+            localMessage.setSubject("["
+                    + repositoryDestinationBuffer.toString() + "] "
+                    + localMessage.getSubject());
+            localMessage.saveChanges();
+            // NOTE: recipient will have to change to a MailAddress built
+            // from the repositoryDestinationBuffer
+            aMailetContext
+                    .storeMail(aMail.getSender(), recipient, localMessage);
+            delivered = true;
+        }
+        catch (MessagingException ex)
+        {
+            aMailetContext.log("Error while storing mail.", ex);
+            throw ex;
+        }
+        finally
+        {
+            // Ensure the mail is always ghosted
+            aMail.setState(Mail.GHOST);
+        }
+        if (delivered)
+        {
+            aMailetContext.log("Filed Message ID: "
+                    + aMail.getMessage().getMessageID()
+                    + " into destination: \""
+                    + repositoryDestinationBuffer.toString() + "\"");
+        }
+    }
+
+    /**
+     * <p>
+     * Executes the passed ActionKeep.
+     * </p>
+     * 
+     * <p>
+     * In this implementation, "keep" is equivalent to "fileinto" with a
+     * destination of "INBOX".
+     * </p>
+     * 
+     * @param anAction
+     * @param aMail
+     * @param aMailetContext
+     * @throws MessagingException
+     */
+    public static void execute(ActionKeep anAction, Mail aMail,
+            MailetContext aMailetContext) throws MessagingException
+    {
+        ActionFileInto action = new ActionFileInto("INBOX");
+        execute(action, aMail, aMailetContext);
+    }
+
+    /**
+     * Method execute executes the passed ActionRedirect.
+     * 
+     * @param anAction
+     * @param aMail
+     * @param aMailetContext
+     * @throws MessagingException
+     */
+    public static void execute(ActionRedirect anAction, Mail aMail,
+            MailetContext aMailetContext) throws MessagingException
+    {
+        detectAndHandleLocalLooping(aMail, aMailetContext, "redirect");
+        Collection recipients = new ArrayList(1);
+        recipients.add(new InternetAddress(anAction.getAddress()));
+        aMailetContext.sendMail(aMail.getSender(), recipients, aMail
+                .getMessage());
+        aMail.setState(Mail.GHOST);
+        aMailetContext.log("Redirected Message ID: "
+                + aMail.getMessage().getMessageID() + " to \""
+                + anAction.getAddress() + "\"");
+    }
+
+    /**
+     * <p>
+     * Method execute executes the passed ActionReject. It sends an RFC 2098
+     * compliant reject MDN back to the sender.
+     * </p>
+     * <p>
+     * NOTE: The Mimecontent type should be 'report', but as we do not yet have
+     * a DataHandler for this yet, its currently 'text'!
+     * 
+     * @param anAction
+     * @param aMail
+     * @param aMailetContext
+     * @throws MessagingException
+     */
+    public static void execute(ActionReject anAction, Mail aMail,
+            MailetContext aMailetContext) throws MessagingException
+    {
+        detectAndHandleLocalLooping(aMail, aMailetContext, "reject");
+
+        // Create the MDN part
+        StringBuffer humanText = new StringBuffer(128);
+        humanText
+                .append("This message was refused by the recipient's mail filtering program.");
+        humanText.append("\r\n");
+        humanText.append("The reason given was:");
+        humanText.append("\r\n");
+        humanText.append("\r\n");
+        humanText.append(anAction.getMessage());
+
+        String reporting_UA_name = null;
+        try
+        {
+            reporting_UA_name = InetAddress.getLocalHost()
+                    .getCanonicalHostName();
+        }
+        catch (UnknownHostException ex)
+        {
+            reporting_UA_name = "localhost";
+        }
+
+        String reporting_UA_product = aMailetContext.getServerInfo();
+
+        String[] originalRecipients = aMail.getMessage().getHeader(
+                "Original-Recipient");
+        String original_recipient = null;
+        if (null != originalRecipients && originalRecipients.length > 0)
+        {
+            original_recipient = originalRecipients[0];
+        }
+
+        MailAddress soleRecipient = getSoleRecipient(aMail);
+        String final_recipient = soleRecipient.toString();
+
+        String original_message_id = aMail.getMessage().getMessageID();
+
+        DispositionModifier modifiers[] = {new ModifierError()};
+        Disposition disposition = new Disposition(new ActionModeAutomatic(),
+                new SendingModeAutomatic(), new TypeDeleted(), modifiers);
+
+        MimeMultipart multiPart = MDNFactory.create(humanText.toString(),
+                reporting_UA_name, reporting_UA_product, original_recipient,
+                final_recipient, original_message_id, disposition);
+
+        // Send the message
+        MimeMessage reply = (MimeMessage) aMail.getMessage().reply(false);
+        reply.setFrom(soleRecipient.toInternetAddress());
+        reply.setContent(multiPart);
+        reply.saveChanges();
+        Address[] recipientAddresses = reply.getAllRecipients();
+        if (null != recipientAddresses)
+        {
+            Collection recipients = new ArrayList(recipientAddresses.length);
+            for (int i = 0; i < recipientAddresses.length; i++)
+            {
+                recipients.add(new MailAddress(
+                        (InternetAddress) recipientAddresses[i]));
+            }
+            aMailetContext.sendMail(null, recipients, reply);
+        }
+        else
+        {
+            aMailetContext
+                    .log("Unable to send reject MDN. Could not determine the recipient.");
+        }
+        // Ghost the original mail
+        aMail.setState(Mail.GHOST);
+    }
+
+    /**
+     * Answers the sole intended recipient for aMail.
+     * 
+     * @param aMail
+     * @return String
+     * @throws MessagingException
+     */
+    protected static MailAddress getSoleRecipient(Mail aMail)
+            throws MessagingException
+    {
+        if (1 != aMail.getRecipients().size())
+            throw new MessagingException("Invalid number of recipients - "
+                    + new Integer(aMail.getRecipients().size()).toString()
+                    + ". Exactly 1 recipient is expected.");
+        return (MailAddress) aMail.getRecipients().iterator().next();
+    }
+
+    /**
+     * Returns a lazy initialised attributePrefix.
+     * 
+     * @return String
+     */
+    protected static String getAttributePrefix()
+    {
+        String value = null;
+        if (null == (value = getAttributePrefixBasic()))
+        {
+            updateAttributePrefix();
+            return getAttributePrefix();
+        }
+        return value;
+    }
+
+    /**
+     * Returns the attributePrefix.
+     * 
+     * @return String
+     */
+    private static String getAttributePrefixBasic()
+    {
+        return fieldAttributePrefix;
+    }
+
+    /**
+     * Returns the computed attributePrefix.
+     * 
+     * @return String
+     */
+    protected static String computeAttributePrefix()
+    {
+        return Actions.class.getPackage().getName() + ".";
+    }
+
+    /**
+     * Sets the attributePrefix.
+     * 
+     * @param attributePrefix The attributePrefix to set
+     */
+    protected static void setAttributePrefix(String attributePrefix)
+    {
+        fieldAttributePrefix = attributePrefix;
+    }
+
+    /**
+     * Updates the attributePrefix.
+     */
+    protected static void updateAttributePrefix()
+    {
+        setAttributePrefix(computeAttributePrefix());
+    }
+
+    /**
+     * Detect and handle locally looping mail. External loop detection is left
+     * to the MTA.
+     * 
+     * @param aMail
+     * @param aMailetContext
+     * @param anAttributeSuffix
+     * @throws MessagingException
+     */
+    protected static void detectAndHandleLocalLooping(Mail aMail,
+            MailetContext aMailetContext, String anAttributeSuffix)
+            throws MessagingException
+    {
+        MailAddress thisRecipient = getSoleRecipient(aMail);
+        MailAddress lastRecipient = (MailAddress) aMail
+                .getAttribute(getAttributePrefix() + anAttributeSuffix);
+        if (null != lastRecipient && lastRecipient.equals(thisRecipient))
+        {
+            MessagingException ex = new MessagingException(
+                    "This message is looping! Message ID: "
+                            + aMail.getMessage().getMessageID());
+            aMailetContext.log(ex.getMessage(), ex);
+            throw ex;
+        }
+        aMail.setAttribute(getAttributePrefix() + anAttributeSuffix,
+                thisRecipient);
+    }
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/DestinationException.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/DestinationException.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,55 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james;
+
+import javax.mail.MessagingException;
+
+/**
+ * Class <code>DestinationException</code> is thrown when a target
+ * destionaition is invalid.
+ */
+public class DestinationException extends MessagingException
+{
+
+    /**
+     * Constructor for DestinationException.
+     */
+    public DestinationException()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for DestinationException.
+     * @param arg0
+     */
+    public DestinationException(String arg0)
+    {
+        super(arg0);
+    }
+
+    /**
+     * Constructor for DestinationException.
+     * @param arg0
+     * @param arg1
+     */
+    public DestinationException(String arg0, Exception arg1)
+    {
+        super(arg0, arg1);
+    }
+
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/JSieve.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/JSieve.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,363 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+import javax.mail.MessagingException;
+
+import org.apache.james.core.MailImpl;
+import org.apache.jsieve.SieveException;
+import org.apache.jsieve.SieveFactory;
+import org.apache.jsieve.mail.MailAdapter;
+import org.apache.jsieve.parser.generated.Node;
+import org.apache.jsieve.parser.generated.ParseException;
+import org.apache.mailet.GenericMailet;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetException;
+
+/**
+ * <p>
+ * Class JSieve is a DEMONSTRATION Mailet that invokes JSieve to 
+ * perform mail processing. There is a single configuration parameter,
+ * <code>scriptURL</code>. This a URL pointing to the resource containing
+ * the Sieve script to run.
+ * </p>
+ * 
+ * <p>
+ * This Mailet is intended to replace the <code>LocalDelivery</code>
+ * Mailet in James. Sieve's Keep Action is functionally equivalent to the
+ * <code>LocalDelivery</code> Mailet's processing. The Sieve script may
+ * explicitily or implicitly invoke this Action, and/or any other 
+ * configured Action. Actions are configured in class
+ * <code>ActionDispatcher</code>. 
+ * </p>
+ * 
+ * <p>IMPORTANT NOTES</p>
+ * 
+ * <p>
+ * This is NOT production quality code! It is a test harness for exercising
+ * jSieve. At least the feutures listed below would be required to consider
+ * this of production quality.
+ * </p>
+ * 
+ * <p>REQUIRED FEATURES</p>
+ * 
+ * <p>
+ * To be truly useful, this mailet needs to be configurable to invoke
+ * user specific Sieve scripts so that indivual users have control of their
+ * mail processing.
+ * </p>
+ * 
+ * <p>
+ * In a Mailet environment, a generic Sieve command to invoke a Mailet
+ * would be extremely powerful as jSieve could then leverage the abilities
+ * of all available Mailets. Currently, Sieve must wastefully duplicate 
+ * the same behaviour as Sieve commands.
+ * </p> 
+ * 
+ * <p>The converse also applies. Provision should be made for a Mailet to 
+ * reuse jSieve commands. As the primary difference between the two is 
+ * that Sieve deals with a single recipient while Mailets deal with
+ * multiple recipients, a Mailet could simply iterate over all of its
+ * recipients invoking the Sieve command for each recipient.  
+ * </p>
+ */
+public class JSieve extends GenericMailet
+{
+    private static final Random random = new Random();
+    private URL fieldScriptURL;
+    private Node fieldStartNode;
+
+    /**
+     * Constructor for JSieve.
+     */
+    public JSieve()
+    {
+        super();
+    }
+
+    /**
+     * @see org.apache.mailet.Mailet#service(Mail)
+     */
+    public void service(Mail mail) throws MessagingException
+    {
+        // If the mail has no recipients, do nothing
+        if (mail.getRecipients().isEmpty())
+            return;
+        // Sieve expects a single recipient. If the mail has more we need to
+        // clone the mail, with each mail having a single recipient and
+        // resend them.
+        if (mail.getRecipients().size() == 1)
+        {
+            // Evaluate the mail against the script.
+            // The default state for the mail is GHOST.
+            // Actions executed as a result of evaluating the script may
+            // change this.
+            mail.setState(Mail.GHOST);
+            evaluate(mail);
+        }
+        else
+        {
+            Iterator recipientsIter = mail.getRecipients().iterator();
+            List recipients = new ArrayList(1);
+            while (recipientsIter.hasNext())
+            {
+                //                MailImpl mailClone = duplicate((MailImpl) mail);
+                recipients.clear();
+                recipients.add(recipientsIter.next());
+                //               mailClone.setRecipients(recipients);
+                //               getMailetContext().sendMail(mailClone);
+                getMailetContext().sendMail(mail.getSender(), recipients,
+                        mail.getMessage(), mail.getState());
+            }
+            // Kill the original message
+            mail.setState(Mail.GHOST);
+        }
+    }
+
+    protected MailImpl duplicate(MailImpl aMail) throws MessagingException
+    {
+        // duplicates the Mail object, to be able to modify the new mail
+        // keeping
+        // the original untouched
+        MailImpl newMail = (MailImpl) aMail.duplicate(newName(aMail));
+        // We don't need to use the original Remote Address and Host,
+        // and doing so would likely cause a loop with spam detecting
+        // matchers.
+        try
+        {
+            ((MailImpl) newMail).setRemoteAddr(java.net.InetAddress
+                    .getLocalHost().getHostAddress());
+            ((MailImpl) newMail).setRemoteHost(java.net.InetAddress
+                    .getLocalHost().getHostName());
+        }
+        catch (java.net.UnknownHostException _)
+        {
+            ((MailImpl) newMail).setRemoteAddr("127.0.0.1");
+            ((MailImpl) newMail).setRemoteHost("localhost");
+        }
+        return newMail;
+    }
+
+    /**
+     * Create a unique new primary key name.
+     * 
+     * Borrowed from org.apache.james.transport.mailets.AbstractRedirect
+     * 
+     * @param mail the mail to use as the basis for the new mail name
+     * @return a new name
+     */
+    private String newName(MailImpl mail) throws MessagingException
+    {
+        String oldName = mail.getName();
+        // Checking if the original mail name is too long, perhaps because of a
+        // loop caused by a configuration error.
+        // it could cause a "null pointer exception" in AvalonMailRepository
+        // much
+        // harder to understand.
+        if (oldName.length() > 76)
+        {
+            int count = 0;
+            int index = 0;
+            while ((index = oldName.indexOf('!', index + 1)) >= 0)
+            {
+                count++;
+            }
+            // It looks like a configuration loop. It's better to stop.
+            if (count > 7)
+            {
+                throw new MessagingException(
+                        "Unable to create a new message name: too long."
+                                + " Possible loop in config.xml.");
+            }
+            else
+            {
+                oldName = oldName.substring(0, 76);
+            }
+        }
+        StringBuffer nameBuffer = new StringBuffer(64).append(oldName).append(
+                "-!").append(random.nextInt(1048576));
+        return nameBuffer.toString();
+    }
+
+    /**
+     * Method evaluate evaluates the receivers script against aMail.
+     * 
+     * @param aMail
+     * @throws MessagingException
+     */
+    protected void evaluate(Mail aMail) throws MessagingException
+    {
+        // Evaluate the script against the mail
+        try
+        {
+            MailAdapter aMailAdapter = new SieveMailAdapter(aMail,
+                    getMailetContext());
+            log("Evaluating " + aMailAdapter.toString() + "against \""
+                    + getScriptURL().toExternalForm() + "\"");
+            SieveFactory.getInstance().evaluate(aMailAdapter, getStartNode());
+        }
+        catch (SieveException ex)
+        {
+            log("Exception evaluating Sieve script", ex);
+            // If there were errors, we redirect the email to the ERROR
+            // processor.
+            // In order for this server to meet the requirements of the SMTP
+            // specification,
+            // mails on the ERROR processor must be returned to the sender.
+            // Note that this
+            // email doesn't include any details regarding the details of the
+            // failure(s).
+            // In the future we may wish to address this.
+            getMailetContext().sendMail(aMail.getSender(),
+                    aMail.getRecipients(), aMail.getMessage(), Mail.ERROR);
+            throw new MessagingException("Exception evaluating Sieve script",
+                    ex);
+        }
+    }
+
+    /**
+     * Returns the scriptURL.
+     * 
+     * @return URL
+     */
+    public URL getScriptURL()
+    {
+        return fieldScriptURL;
+    }
+
+    /**
+     * Sets the scriptURL.
+     * 
+     * @param scriptURL The scriptURL to set
+     */
+    protected void setScriptURL(URL scriptURL)
+    {
+        fieldScriptURL = scriptURL;
+    }
+
+    /**
+     * Returns the startNode, lazy initialised if required.
+     * 
+     * @return Node
+     * @throws MessagingException
+     */
+    public Node getStartNode() throws MessagingException
+    {
+        Node node = null;
+        if (null == (node = getStartNodeBasic()))
+        {
+            updateStartNode();
+            return getStartNode();
+        }
+        return node;
+    }
+
+    /**
+     * Returns the startNode.
+     * 
+     * @return Node
+     */
+    private Node getStartNodeBasic()
+    {
+        return fieldStartNode;
+    }
+
+    /**
+     * Parses the script and returns its startNode.
+     * 
+     * @return Node
+     * @throws MessagingException
+     */
+    protected Node computeStartNode() throws MessagingException
+    {
+        Object content = null;
+        try
+        {
+            content = getScriptURL().getContent();
+            if (!(content instanceof InputStream))
+            {
+                String msg = "Cannot get an InputStream for "
+                        + getScriptURL().toExternalForm();
+                log(msg);
+                throw new MessagingException(msg);
+            }
+            return SieveFactory.getInstance().parse(
+                    new BufferedInputStream((InputStream) content));
+        }
+        catch (IOException ex)
+        {
+            String msg = "Exception getting contents of script URL: "
+                    + getScriptURL().toExternalForm();
+            log(msg, ex);
+            throw new MessagingException(msg, ex);
+        }
+        catch (ParseException ex)
+        {
+            String msg = "Exception parsing Sieve script: "
+                    + getScriptURL().toExternalForm();
+            log(msg, ex);
+            throw new MessagingException(msg, ex);
+        }
+    }
+
+    /**
+     * Sets the startNode.
+     * 
+     * @param startNode The startNode to set
+     */
+    protected void setStartNode(Node startNode)
+    {
+        fieldStartNode = startNode;
+    }
+
+    /**
+     * Updates the startNode.
+     * 
+     * @throws MessagingException
+     */
+    protected void updateStartNode() throws MessagingException
+    {
+        setStartNode(computeStartNode());
+    }
+
+    /**
+     * @see org.apache.mailet.GenericMailet#init()
+     */
+    public void init() throws MessagingException
+    {
+        super.init();
+        try
+        {
+            setScriptURL(new URL(getInitParameter("scriptURL")));
+        }
+        catch (MalformedURLException e)
+        {
+            throw new MailetException(
+                    "Error in configuration parameter \"scriptURL\"", e);
+        }
+    }
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/SieveMailAdapter.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/SieveMailAdapter.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,371 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import javax.mail.Header;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import org.apache.jsieve.SieveException;
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.MailAdapter;
+import org.apache.jsieve.mail.MailUtils;
+import org.apache.jsieve.mail.SieveMailException;
+import org.apache.jsieve.mail.optional.EnvelopeAccessors;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+/**
+ * <p>
+ * Class <code>SieveMailAdapter</code> implements a <code>MailAdapter</code>
+ * for use in a Mailet environment.
+ * </p>
+ */
+public class SieveMailAdapter implements MailAdapter, EnvelopeAccessors
+{
+    /**
+     * The Mail being adapted.
+     */
+    private Mail fieldMail;
+    /**
+     * The MailetContext.
+     */
+    private MailetContext fieldMailetContext;
+    /**
+     * List of Actions to perform.
+     */
+    private List fieldActions;
+    /**
+     * Constructor for SieveMailAdapter.
+     */
+    private SieveMailAdapter()
+    {
+        super();
+    }
+    /**
+     * Constructor for SieveMailAdapter.
+     * 
+     * @param aMail
+     * @param aMailetContext
+     */
+    public SieveMailAdapter(Mail aMail, MailetContext aMailetContext)
+    {
+        this();
+        setMail(aMail);
+        setMailetContext(aMailetContext);
+    }
+    /**
+     * Returns the message.
+     * 
+     * @return MimeMessage
+     */
+    protected MimeMessage getMessage() throws MessagingException
+    {
+        return getMail().getMessage();
+    }
+    /**
+     * Returns the List of actions.
+     * 
+     * @return List
+     */
+    public List getActions()
+    {
+        List actions = null;
+        if (null == (actions = getActionsBasic()))
+        {
+            updateActions();
+            return getActions();
+        }
+        return actions;
+    }
+    /**
+     * Returns a new List of actions.
+     * 
+     * @return List
+     */
+    protected List computeActions()
+    {
+        return new ArrayList();
+    }
+    /**
+     * Returns the List of actions.
+     * 
+     * @return List
+     */
+    private List getActionsBasic()
+    {
+        return fieldActions;
+    }
+    /**
+     * Adds an Action.
+     * 
+     * @param action The action to set
+     */
+    public void addAction(Action action)
+    {
+        getActions().add(action);
+    }
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#executeActions()
+     */
+    public void executeActions() throws SieveException
+    {
+        //        Log log = Logger.getLog();
+        //        boolean isDebugEnabled = log.isDebugEnabled();
+        ListIterator actionsIter = getActionsIterator();
+        while (actionsIter.hasNext())
+        {
+            Action action = (Action) actionsIter.next();
+            getMailetContext().log("Executing action: " + action.toString());
+            try
+            {
+                ActionDispatcher.getInstance().execute(action, getMail(),
+                        getMailetContext());
+            }
+            catch (NoSuchMethodException e)
+            {
+                throw new SieveException(e.getMessage());
+            }
+            catch (IllegalAccessException e)
+            {
+                throw new SieveException(e.getMessage());
+            }
+            catch (InvocationTargetException e)
+            {
+                throw new SieveException(e.getMessage());
+            }
+            catch (MessagingException e)
+            {
+                throw new SieveException(e.getMessage());
+            }
+        }
+    }
+    /**
+     * Sets the actions.
+     * 
+     * @param actions The actions to set
+     */
+    protected void setActions(List actions)
+    {
+        fieldActions = actions;
+    }
+    /**
+     * Updates the actions.
+     */
+    protected void updateActions()
+    {
+        setActions(computeActions());
+    }
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getActionsIterator()
+     */
+    public ListIterator getActionsIterator()
+    {
+        return getActions().listIterator();
+    }
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getHeader(String)
+     */
+    public List getHeader(String name) throws SieveMailException
+    {
+        try
+        {
+            String[] headers = getMessage().getHeader(name);
+            return (headers == null ? new ArrayList(0) : Arrays.asList(headers));
+        }
+        catch (MessagingException ex)
+        {
+            throw new SieveMailException(ex);
+        }
+    }
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getHeaderNames()
+     */
+    public List getHeaderNames() throws SieveMailException
+    {
+        Set headerNames = new HashSet();
+        try
+        {
+            Enumeration allHeaders = getMessage().getAllHeaders();
+            while (allHeaders.hasMoreElements())
+            {
+                headerNames.add(((Header) allHeaders.nextElement()).getName());
+            }
+            return new ArrayList(headerNames);
+        }
+        catch (MessagingException ex)
+        {
+            throw new SieveMailException(ex);
+        }
+    }
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getMatchingHeader(String)
+     */
+    public List getMatchingHeader(String name) throws SieveMailException
+    {
+        return MailUtils.getMatchingHeader(this, name);
+    }
+    /**
+     * @see org.apache.jsieve.mail.MailAdapter#getSize()
+     */
+    public int getSize() throws SieveMailException
+    {
+        try
+        {
+            return getMessage().getSize();
+        }
+        catch (MessagingException ex)
+        {
+            throw new SieveMailException(ex);
+        }
+    }
+    /**
+     * Method getEnvelopes.
+     * 
+     * @return Map
+     */
+    protected Map getEnvelopes()
+    {
+        Map envelopes = new HashMap(2);
+        if (null != getEnvelopeFrom())
+            envelopes.put("From", getEnvelopeFrom());
+        if (null != getEnvelopeTo())
+            envelopes.put("To", getEnvelopeTo());
+        return envelopes;
+    }
+    /**
+     * @see org.apache.jsieve.mail.optional.EnvelopeAccessors#getEnvelope(String)
+     */
+    public List getEnvelope(String name) throws SieveMailException
+    {
+        List values = new ArrayList(1);
+        Object value = getEnvelopes().get(name);
+        if (null != value)
+            values.add(value);
+        return values;
+    }
+    /**
+     * @see org.apache.jsieve.mail.optional.EnvelopeAccessors#getEnvelopeNames()
+     */
+    public List getEnvelopeNames() throws SieveMailException
+    {
+        return new ArrayList(getEnvelopes().keySet());
+    }
+    /**
+     * @see org.apache.jsieve.mail.optional.EnvelopeAccessors#getMatchingEnvelope(String)
+     */
+    public List getMatchingEnvelope(String name) throws SieveMailException
+    {
+        Iterator envelopeNamesIter = getEnvelopeNames().iterator();
+        List matchedEnvelopeValues = new ArrayList(32);
+        while (envelopeNamesIter.hasNext())
+        {
+            String envelopeName = (String) envelopeNamesIter.next();
+            if (envelopeName.trim().equalsIgnoreCase(name))
+                matchedEnvelopeValues.addAll(getEnvelope(envelopeName));
+        }
+        return matchedEnvelopeValues;
+    }
+    /**
+     * Returns the from.
+     * 
+     * @return String
+     */
+    public String getEnvelopeFrom()
+    {
+        MailAddress sender = getMail().getSender(); 
+        return (null == sender ? "" : sender.toString());
+    }
+    /**
+     * Returns the sole recipient or null if there isn't one.
+     * 
+     * @return String
+     */
+    public String getEnvelopeTo()
+    {
+        String recipient = null;
+        Iterator recipientIter = getMail().getRecipients().iterator();
+        if (recipientIter.hasNext())
+            recipient = (String) recipientIter.next().toString();
+        return recipient;
+    }
+    /**
+     * Returns the mail.
+     * 
+     * @return Mail
+     */
+    public Mail getMail()
+    {
+        return fieldMail;
+    }
+    /**
+     * Sets the mail.
+     * 
+     * @param mail The mail to set
+     */
+    protected void setMail(Mail mail)
+    {
+        fieldMail = mail;
+    }
+    /**
+     * Returns the mailetContext.
+     * 
+     * @return MailetContext
+     */
+    public MailetContext getMailetContext()
+    {
+        return fieldMailetContext;
+    }
+    /**
+     * Sets the mailetContext.
+     * 
+     * @param mailetContext The mailetContext to set
+     */
+    protected void setMailetContext(MailetContext mailetContext)
+    {
+        fieldMailetContext = mailetContext;
+    }
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString()
+    {
+        String messageID = null;
+        try
+        {
+            messageID = getMail().getMessage().getMessageID();
+        }
+        catch (MessagingException e)
+        {
+            messageID = "<" + e.getMessage() + ">";
+        }
+        return getClass().getName() + " Envelope From: "
+                + (null == getEnvelopeFrom() ? "null" : getEnvelopeFrom())
+                + " Envelope To: "
+                + (null == getEnvelopeTo() ? "null" : getEnvelopeTo())
+                + " Message ID: " + (null == messageID ? "null" : messageID);
+    }
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/ActionDispatcherTest.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/ActionDispatcherTest.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,132 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james.junit;
+
+import java.lang.reflect.InvocationTargetException;
+
+import javax.mail.MessagingException;
+
+import junit.framework.TestCase;
+
+import org.apache.james.core.MailImpl;
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.ActionKeep;
+import org.apache.jsieve.samples.james.ActionDispatcher;
+import org.apache.jsieve.samples.james.junit.utils.ActionAbsent;
+import org.apache.jsieve.samples.james.junit.utils.MockMailetContext;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetContext;
+
+/**
+ * Class <code>ActionDispatcherTest</code>.
+ */
+public class ActionDispatcherTest extends TestCase
+{
+
+    /**
+     * Constructor for ActionDispatcherTest.
+     * @param arg0
+     */
+    public ActionDispatcherTest(String arg0)
+    {
+        super(arg0);
+    }
+
+    public static void main(String[] args)
+    {
+        junit.swingui.TestRunner.run(ActionDispatcherTest.class);
+    }
+
+    /**
+     * @see TestCase#setUp()
+     */
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        ActionDispatcher.resetInstance();
+    }
+
+    /**
+     * @see TestCase#tearDown()
+     */
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+    
+    /**
+     * Test execute of ActionKeep
+     */
+    public void testExecuteActionKeep()
+    {
+        boolean isTestPassed = false;
+        Mail aMail = new MailImpl();
+        MailetContext aMailetContext = new MockMailetContext();
+        Action action = new ActionKeep();
+        try
+        {
+            ActionDispatcher.getInstance().execute(
+                action,
+                aMail,
+                aMailetContext);
+            isTestPassed = true;
+        }
+        catch (NoSuchMethodException e)
+        {
+        }
+        catch (IllegalAccessException e)
+        {
+        }
+        catch (InvocationTargetException e)
+        {
+        }
+        catch (MessagingException e)
+        {
+        }
+        assertTrue(isTestPassed);
+    }
+    
+    /**
+     * Test execute of ActionAbsent. Should throw a NoSuchMethodException.
+     */
+    public void testExecuteActionAbsent()
+    {
+        boolean isTestPassed = false;
+        Mail aMail = new MailImpl();
+        MailetContext aMailetContext = new MockMailetContext();                         
+        Action action = new ActionAbsent();
+        try
+        {
+            ActionDispatcher.getInstance().execute(action, aMail, aMailetContext);
+        }
+        catch (NoSuchMethodException e)
+        {
+            isTestPassed = true;            
+        }
+        catch (IllegalAccessException e)
+        {
+        }
+        catch (InvocationTargetException e)
+        {
+        }
+        catch (MessagingException e)
+        {
+        }        
+        assertTrue(isTestPassed);        
+    }            
+
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/AllTests.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/AllTests.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,42 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james.junit;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Class <code>AllTests</code>.
+ */
+public class AllTests
+{
+
+    public static void main(String[] args)
+    {
+        junit.swingui.TestRunner.run(AllTests.class);
+    }
+
+    public static Test suite()
+    {
+        TestSuite suite =
+            new TestSuite("Test for org.apache.jsieve.samples.james.junit");
+        //$JUnit-BEGIN$
+        suite.addTest(new TestSuite(ActionDispatcherTest.class));          
+        //$JUnit-END$
+        return suite;
+    }
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/utils/ActionAbsent.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/utils/ActionAbsent.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,35 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james.junit.utils;
+
+import org.apache.jsieve.mail.Action;
+
+/**
+ * Class <code>ActionAbsent</code>.
+ */
+public class ActionAbsent implements Action
+{
+
+    /**
+     * Constructor for ActionAbsent.
+     */
+    public ActionAbsent()
+    {
+        super();
+    }
+
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/utils/MockMailetContext.java
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/junit/utils/MockMailetContext.java	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,214 @@
+/***********************************************************************
+ * Copyright (c) 2003-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+package org.apache.jsieve.samples.james.junit.utils;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+
+/**
+ * Class <code>MockMailetContext</code>.
+ */
+public class MockMailetContext implements MailetContext
+{
+
+    /**
+     * Constructor for MockMailetContext.
+     */
+    public MockMailetContext()
+    {
+        super();
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#bounce(Mail, String)
+     */
+    public void bounce(Mail mail, String message) throws MessagingException
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#bounce(Mail, String, MailAddress)
+     */
+    public void bounce(Mail mail, String message, MailAddress bouncer)
+        throws MessagingException
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#getMailServers(String)
+     */
+    public Collection getMailServers(String host)
+    {
+        return null;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#getPostmaster()
+     */
+    public MailAddress getPostmaster()
+    {
+        return null;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#getAttribute(String)
+     */
+    public Object getAttribute(String name)
+    {
+        return null;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#getAttributeNames()
+     */
+    public Iterator getAttributeNames()
+    {
+        return null;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#getMajorVersion()
+     */
+    public int getMajorVersion()
+    {
+        return 0;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#getMinorVersion()
+     */
+    public int getMinorVersion()
+    {
+        return 0;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#getServerInfo()
+     */
+    public String getServerInfo()
+    {
+        return null;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#isLocalServer(String)
+     */
+    public boolean isLocalServer(String serverName)
+    {
+        return false;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#isLocalUser(String)
+     */
+    public boolean isLocalUser(String userAccount)
+    {
+        return false;
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#log(String)
+     */
+    public void log(String message)
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#log(String, Throwable)
+     */
+    public void log(String message, Throwable t)
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#removeAttribute(String)
+     */
+    public void removeAttribute(String name)
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#sendMail(MimeMessage)
+     */
+    public void sendMail(MimeMessage msg) throws MessagingException
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#sendMail(MailAddress, Collection, MimeMessage)
+     */
+    public void sendMail(
+        MailAddress sender,
+        Collection recipients,
+        MimeMessage msg)
+        throws MessagingException
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#sendMail(MailAddress, Collection, MimeMessage, String)
+     */
+    public void sendMail(
+        MailAddress sender,
+        Collection recipients,
+        MimeMessage msg,
+        String state)
+        throws MessagingException
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#sendMail(Mail)
+     */
+    public void sendMail(Mail mail) throws MessagingException
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#setAttribute(String, Object)
+     */
+    public void setAttribute(String name, Object object)
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#storeMail(MailAddress, MailAddress, MimeMessage)
+     */
+    public void storeMail(
+        MailAddress sender,
+        MailAddress recipient,
+        MimeMessage msg)
+        throws MessagingException
+    {
+    }
+
+    /**
+     * @see org.apache.mailet.MailetContext#getSMTPHostAddresses(java.lang.String)
+     */
+    public Iterator getSMTPHostAddresses(String arg0)
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}

Added: james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/package.html
==============================================================================
--- (empty file)
+++ james/jsieve/trunk/src/java/org/apache/jsieve/samples/james/package.html	Sun Apr 18 10:50:58 2004
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<!--
+
+  @(#)package.html
+
+   Copyright (c) 2003-2004 The Apache Software Foundation.
+   All rights reserved.
+   -------------------------------------------------------------------
+   Licensed under the Apache License, Version 2.0 (the "License"); you
+   may not use this file except in compliance with the License. You
+   may obtain a copy of the License at:
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied.  See the License for the specific language governing
+   permissions and limitations under the License.
+
+-->
+
+</HEAD>
+<BODY>
+
+<p>This package implements.a sample implementation of a Mailet that invokes
+jSieve to perform local mail delivery processing. The Mailet is implemented
+by Class <code>JSieve</code>. The remaining classes implement supporting
+behaviours. See the class comments for details.
+</p>
+<p>This code is for DEMONSTRATION purposes only and should not be considered
+to be of production quality.
+</p>
+<p>Note that there is currently a dependency on 
+<code>org.apache.james.utils.mail.mdn.MDNFactory</code>. This binds the 
+implementation to versions of Apache James that include this class. To achieve
+a truly portable Mailet implementation, the Mailet API may need to be extended
+to include the ability to create an MDN.
+</p>
+</BODY>
+</HTML>
\ No newline at end of file

---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org