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 }