1 /************************************************************************
2 * Copyright (c) 2003-2006 The Apache Software Foundation. *
3 * All rights reserved. *
4 * ------------------------------------------------------------------- *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you *
6 * may not use this file except in compliance with the License. You *
7 * may obtain a copy of the License at: *
8 * *
9 * http://www.apache.org/licenses/LICENSE-2.0 *
10 * *
11 * Unless required by applicable law or agreed to in writing, software *
12 * distributed under the License is distributed on an "AS IS" BASIS, *
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14 * implied. See the License for the specific language governing *
15 * permissions and limitations under the License. *
16 ***********************************************************************/
17
18 package org.apache.james.transport.matchers;
19
20 import java.util.Iterator;
21 import java.util.Collection;
22 import java.util.ArrayList;
23 import javax.mail.MessagingException;
24 import org.apache.mailet.GenericMatcher;
25 import org.apache.mailet.MailAddress;
26 import org.apache.mailet.Mail;
27
28 /***
29 * <P>Abstract matcher checking whether a recipient has exceeded a maximum allowed quota.</P>
30 * <P>"Quota" at this level is an abstraction whose specific interpretation
31 * will be done by subclasses.</P>
32 * <P>Although extending GenericMatcher, its logic is recipient oriented.</P>
33 *
34 * @version CVS $Revision: 365582 $ $Date: 2006-01-03 08:51:21 +0000 (mar, 03 gen 2006) $
35 * @since 2.2.0
36 */
37 abstract public class AbstractQuotaMatcher extends GenericMatcher {
38
39 /***
40 * Standard matcher entrypoint.
41 * First of all, checks the sender using {@link #isSenderChecked}.
42 * Then, for each recipient checks it using {@link #isRecipientChecked} and
43 * {@link #isOverQuota}.
44 *
45 * @throws MessagingException if either <CODE>isSenderChecked</CODE> or isRecipientChecked throw an exception
46 */
47 public final Collection match(Mail mail) throws MessagingException {
48 Collection matching = null;
49 if (isSenderChecked(mail.getSender())) {
50 matching = new ArrayList();
51 for (Iterator i = mail.getRecipients().iterator(); i.hasNext(); ) {
52 MailAddress recipient = (MailAddress) i.next();
53 if (isRecipientChecked(recipient) && isOverQuota(recipient, mail)) {
54 matching.add(recipient);
55 }
56 }
57 }
58 return matching;
59 }
60
61 /***
62 * Does the quota check.
63 * Checks if {@link #getQuota} < {@link #getUsed} for a recipient.
64 * Catches any throwable returning false, and so should any override do.
65 *
66 * @param address the recipient addresss to check
67 * @param mail the mail involved in the check
68 * @return true if over quota
69 */
70 protected boolean isOverQuota(MailAddress address, Mail mail) {
71 String user = address.getUser();
72 try {
73 boolean over = getQuota(address, mail) < getUsed(address, mail);
74 if (over) log(address + " is over quota.");
75 return over;
76 } catch (Throwable e) {
77 log("Exception checking quota for: " + address, e);
78 return false;
79 }
80 }
81
82 /***
83 * Checks the sender.
84 * The default behaviour is to check that the sender <I>is not</I> null nor the local postmaster.
85 * If a subclass overrides this method it should "and" <CODE>super.isSenderChecked</CODE>
86 * to its check.
87 *
88 * @param sender the sender to check
89 */
90 protected boolean isSenderChecked(MailAddress sender) throws MessagingException {
91 return !(sender == null || getMailetContext().getPostmaster().equals(sender));
92 }
93
94 /***
95 * Checks the recipient.
96 * The default behaviour is to check that the recipient <I>is not</I> the local postmaster.
97 * If a subclass overrides this method it should "and" <CODE>super.isRecipientChecked</CODE>
98 * to its check.
99 *
100 * @param recipient the recipient to check
101 */
102 protected boolean isRecipientChecked(MailAddress recipient) throws MessagingException {
103 return !(getMailetContext().getPostmaster().equals(recipient));
104 }
105
106 /***
107 * Gets the quota to check against.
108 *
109 * @param address the address holding the quota if applicable
110 * @param mail the mail involved if needed
111 */
112 abstract protected long getQuota(MailAddress address, Mail mail) throws MessagingException;
113
114 /***
115 * Gets the used amount to check against the quota.
116 *
117 * @param address the address involved
118 * @param mail the mail involved if needed
119 */
120 abstract protected long getUsed(MailAddress address, Mail mail) throws MessagingException;
121
122 /***
123 * Utility method that parses an amount string.
124 * You can use 'k' and 'm' as optional postfixes to the amount (both upper and lowercase).
125 * In other words, "1m" is the same as writing "1024k", which is the same as
126 * "1048576".
127 *
128 * @param amount the amount string to parse
129 */
130 protected long parseQuota(String amount) throws MessagingException {
131 long quota;
132 try {
133 if (amount.endsWith("k")) {
134 amount = amount.substring(0, amount.length() - 1);
135 quota = Long.parseLong(amount) * 1024;
136 } else if (amount.endsWith("m")) {
137 amount = amount.substring(0, amount.length() - 1);
138 quota = Long.parseLong(amount) * 1024 * 1024;
139 } else {
140 quota = Long.parseLong(amount);
141 }
142 return quota;
143 }
144 catch (Exception e) {
145 throw new MessagingException("Exception parsing quota", e);
146 }
147 }
148 }