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  
22  package org.apache.james.transport.matchers;
23  
24  import java.util.Iterator;
25  import java.util.Collection;
26  import java.util.ArrayList;
27  import javax.mail.MessagingException;
28  import org.apache.mailet.base.GenericMatcher;
29  import org.apache.mailet.MailAddress;
30  import org.apache.mailet.Mail;
31  
32  /**
33   * <P>Abstract matcher checking whether a recipient has exceeded a maximum allowed quota.</P>
34   * <P>"Quota" at this level is an abstraction whose specific interpretation
35   * will be done by subclasses.</P> 
36   * <P>Although extending GenericMatcher, its logic is recipient oriented.</P>
37   *
38   * @version CVS $Revision: 713949 $ $Date: 2008-11-14 07:40:21 +0000 (Fri, 14 Nov 2008) $
39   * @since 2.2.0
40   */
41  abstract public class AbstractQuotaMatcher extends GenericMatcher { 
42  
43      /**
44       * Standard matcher entrypoint.
45       * First of all, checks the sender using {@link #isSenderChecked}.
46       * Then, for each recipient checks it using {@link #isRecipientChecked} and
47       * {@link #isOverQuota}.
48       *
49       * @throws MessagingException if either <CODE>isSenderChecked</CODE> or isRecipientChecked throw an exception
50       */    
51      public final Collection match(Mail mail) throws MessagingException {
52          Collection matching = null;
53          if (isSenderChecked(mail.getSender())) {
54              matching = new ArrayList();
55              for (Iterator i = mail.getRecipients().iterator(); i.hasNext(); ) {
56                  MailAddress recipient = (MailAddress) i.next();
57                  if (isRecipientChecked(recipient) && isOverQuota(recipient, mail)) {
58                      matching.add(recipient);
59                  }
60              }
61          }
62          return matching;
63      }
64  
65      /**
66       * Does the quota check.
67       * Checks if {@link #getQuota} < {@link #getUsed} for a recipient.
68       * Catches any throwable returning false, and so should any override do.
69       *
70       * @param address the recipient addresss to check
71       * @param mail the mail involved in the check
72       * @return true if over quota
73       */
74      protected boolean isOverQuota(MailAddress address, Mail mail) {
75          try {
76              boolean over = getQuota(address, mail) < getUsed(address, mail);
77              if (over) log(address + " is over quota.");
78              return over;
79          } catch (Throwable e) {
80              log("Exception checking quota for: " + address, e);
81              return false;
82          }
83      }
84  
85      /** 
86       * Checks the sender.
87       * The default behaviour is to check that the sender <I>is not</I> null nor the local postmaster.
88       * If a subclass overrides this method it should "and" <CODE>super.isSenderChecked</CODE>
89       * to its check.
90       *
91       * @param sender the sender to check
92       */    
93      protected boolean isSenderChecked(MailAddress sender) throws MessagingException {
94          return !(sender == null || getMailetContext().getPostmaster().equals(sender));
95      }
96  
97      /** 
98       * Checks the recipient.
99       * The default behaviour is to check that the recipient <I>is not</I> the local postmaster.
100      * If a subclass overrides this method it should "and" <CODE>super.isRecipientChecked</CODE>
101      * to its check.
102      *
103      * @param recipient the recipient to check
104      */    
105     protected boolean isRecipientChecked(MailAddress recipient) throws MessagingException {
106         return !(getMailetContext().getPostmaster().equals(recipient));
107     }
108 
109     /** 
110      * Gets the quota to check against.
111      *
112      * @param address the address holding the quota if applicable
113      * @param mail the mail involved if needed
114      */    
115     abstract protected long getQuota(MailAddress address, Mail mail) throws MessagingException;
116     
117     /**
118      * Gets the used amount to check against the quota.
119      *
120      * @param address the address involved
121      * @param mail the mail involved if needed
122      */
123     abstract protected long getUsed(MailAddress address, Mail mail) throws MessagingException;
124 
125     /**
126      * Utility method that parses an amount string.
127      * You can use 'k' and 'm' as optional postfixes to the amount (both upper and lowercase).
128      * In other words, "1m" is the same as writing "1024k", which is the same as
129      * "1048576".
130      *
131      * @param amount the amount string to parse
132      */
133     protected long parseQuota(String amount) throws MessagingException {
134         long quota;
135         try {
136             if (amount.endsWith("k")) {
137                 amount = amount.substring(0, amount.length() - 1);
138                 quota = Long.parseLong(amount) * 1024;
139             } else if (amount.endsWith("m")) {
140                 amount = amount.substring(0, amount.length() - 1);
141                 quota = Long.parseLong(amount) * 1024 * 1024;
142             } else {
143                 quota = Long.parseLong(amount);
144             }
145             return quota;
146         }
147         catch (Exception e) {
148             throw new MessagingException("Exception parsing quota", e);
149         }
150     }
151 }