View Javadoc

1   /*****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  
21  package org.apache.jsieve;
22  
23  import java.io.InputStream;
24  
25  import org.apache.commons.logging.Log;
26  
27  import org.apache.jsieve.commands.CommandStateManager;
28  import org.apache.jsieve.commands.ConditionManager;
29  import org.apache.jsieve.mail.*;
30  import org.apache.jsieve.parser.generated.Node;
31  import org.apache.jsieve.parser.generated.ParseException;
32  import org.apache.jsieve.parser.generated.SieveParser;
33  import org.apache.jsieve.parser.generated.SieveParserVisitor;
34  
35  /***
36   * <p>Singleton class SieveFactory is the primary invocation point for all Sieve
37   * operations. Theses are...
38   * <dl>
39   * <dt>parse</dt>
40   * <dd>
41   * Parse a Sieve script into a hierarchy of parsed nodes. A succesful parse means the
42   * script is lexically and gramatically valid according to RFC 3028, section 8. The 
43   * result is the start node of the parsed Sieve script. The start node is resuable.
44   * Typically it is stored for reuse in all subsequent evaluations of the script. 
45   * </dd>
46   * <dt>evaluate</dt>
47   * <dd>
48   * Evaluate an RFC 822 compliant mail message wrapped in a MailAdapter against the 
49   * parse result referenced by the start node from the Parse operation above. As
50   * evaluation proceeds a List of Actions is added to the MailAdapter. At the end of
51   * evaluation, each Action in the List is executed in the order they were added.
52   * </dd>
53   * <dt>interpret/dt>
54   * <dd>A concatenation of parse and evaluate. Useful for testing, but generally the 
55   * parse result should be stored for reuse in subsequent evaluations.  
56   * </dd>
57   * </dl>
58   * </p>
59   * 
60   * 
61   */
62  public class SieveFactory
63  {
64      
65      /***
66       * The sole instance of the receiver.
67       */ 
68      private static SieveFactory fieldInstance;
69  
70      /***
71       * Constructor for SieveFactory.
72       */
73      public SieveFactory()
74      {
75          super();
76      }
77      
78      /***
79       * Method parse parses a Sieve script into a hierarchy of parsed nodes. A 
80       * successful parse means the script is lexically and grammatically valid 
81       * according to RFC 3028, section 8. The result is the start node of the parsed
82       * Sieve script. The start node is reusable. Typically it is stored for reuse in
83       * subsequent evaluations of the script. 
84       * @param inputStream
85       * @return Node
86       * @throws ParseException
87       */
88      public Node parse(InputStream inputStream) throws ParseException
89      {
90          try
91          {
92              return new SieveParser(inputStream, "UTF-8").start();
93          }
94          catch (ParseException ex)
95          {
96              Log log = Logger.getLog();
97              if (log.isErrorEnabled())
98                  log.error("Parse failed. Reason: " + ex.getMessage());
99              if (log.isDebugEnabled())
100                 log.debug("Parse failed.", ex);
101             throw ex;
102         }
103     }
104     
105     /***
106      * <p>Method evaluate evaluates an RFC 822 compliant mail message wrapped in a 
107      * MailAdapter by visting each node of the parsed script beginning at the 
108      * passed start node. As evaluation proceeds a List of Actions is added to the 
109      * MailAdapter.<p>
110      * 
111      * <p>At the start of evaluation an 'implicitKeep' state is set. This can be 
112      * cancelled by a Command during evaluation. If 'implicitKeep' is still set at 
113      * the end of  evaluation, a Keep Action is added to the List of Actions. 
114      * Finally, each Action in the List is executed in the order they were added.
115      * </p>
116      * @param mail
117      * @param startNode
118      * @throws SieveException
119      */
120     public void evaluate(MailAdapter mail, Node startNode)
121         throws SieveException
122     {
123         SieveParserVisitor visitor = new SieveParserVisitorImpl();
124         reset();
125         try
126         {
127             try
128             {
129                 // Evaluate the Nodes
130                 startNode.jjtAccept(visitor, mail);
131 
132             }
133             catch (StopException ex)
134             {
135                 // Stop is OK
136             }
137             catch (SieveException ex)
138             {
139                 Log log = Logger.getLog();
140                 if (log.isErrorEnabled())
141                     log.error("Evaluation failed. Reason: " + ex.getMessage());
142                 if (log.isDebugEnabled())
143                     log.debug("Evaluation failed.", ex);
144                 throw ex;
145             }
146 
147             // If after evaluating all of the nodes or stopping, implicitKeep is still
148             // in effect, add a Keep to the list of Actions.
149             if (CommandStateManager.getInstance().isImplicitKeep())
150                 mail.addAction(new ActionKeep());
151 
152             // Execute the List of Actions   
153             try
154             {
155                 mail.executeActions();
156             }
157             catch (SieveException ex)
158             {
159                 Log log = Logger.getLog();
160                 if (log.isErrorEnabled())
161                     log.error("Evaluation failed. Reason: " + ex.getMessage());
162                 if (log.isDebugEnabled())
163                     log.debug("Evaluation failed.", ex);
164                 throw ex;
165             }
166         } 
167         finally 
168         {
169             // Tidy up managers stored in thread local variables
170             reset();
171         }
172     }
173 
174     private void reset() {
175         ConditionManager.resetInstance();
176         CommandStateManager.resetInstance();
177     }
178     
179     
180     /***
181      * Method interpret parses a Sieve script and then evaluates the result against
182      * a mail.
183      * @param mail
184      * @param inputStream
185      * @throws ParseException
186      * @throws SieveException
187      */
188     public void interpret(MailAdapter mail, InputStream inputStream)
189         throws ParseException, SieveException
190     {
191         evaluate(mail, parse(inputStream));
192     }        
193 
194     /***
195      * Returns the instance of the receiver, lazily initialised if required.
196      * @return SieveFactory
197      */
198     public static synchronized SieveFactory getInstance()
199     {
200         SieveFactory instance = null;
201         if (null == (instance = getInstanceBasic()))
202         {
203             updateInstance();
204             return getInstance();
205         }
206         return instance;
207     }
208     
209     /***
210      * Computes an instance of the receiver.
211      * @return SieveFactory
212      */
213     public static  SieveFactory computeInstance()
214     {
215         return new SieveFactory();
216     }    
217     
218     /***
219      * Returns the instance of the receiver.
220      * @return SieveFactory
221      */
222     private static SieveFactory getInstanceBasic()
223     {
224         return fieldInstance;
225     }    
226 
227     /***
228      * Sets the instance of the receiver.
229      * @param instance The instance to set
230      */
231     protected static void setInstance(SieveFactory instance)
232     {
233         fieldInstance = instance;
234     }
235     
236     /***
237      * Updates the instance of the receiver.
238      */
239     protected static void updateInstance()
240     {
241         setInstance(computeInstance());
242     }    
243 
244 }