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