001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.transport.matchers;
019:
020: import java.util.Iterator;
021: import java.util.Collection;
022: import java.util.ArrayList;
023: import javax.mail.MessagingException;
024: import org.apache.mailet.GenericMatcher;
025: import org.apache.mailet.MailAddress;
026: import org.apache.mailet.Mail;
027:
028: /**
029: * <P>Abstract matcher checking whether a recipient has exceeded a maximum allowed quota.</P>
030: * <P>"Quota" at this level is an abstraction whose specific interpretation
031: * will be done by subclasses.</P>
032: * <P>Although extending GenericMatcher, its logic is recipient oriented.</P>
033: *
034: * @version CVS $Revision: 494012 $ $Date: 2007-01-08 11:23:58 +0100 (Mo, 08 Jan 2007) $
035: * @since 2.2.0
036: */
037: abstract public class AbstractQuotaMatcher extends GenericMatcher {
038:
039: /**
040: * Standard matcher entrypoint.
041: * First of all, checks the sender using {@link #isSenderChecked}.
042: * Then, for each recipient checks it using {@link #isRecipientChecked} and
043: * {@link #isOverQuota}.
044: *
045: * @throws MessagingException if either <CODE>isSenderChecked</CODE> or isRecipientChecked throw an exception
046: */
047: public final Collection match(Mail mail) throws MessagingException {
048: Collection matching = null;
049: if (isSenderChecked(mail.getSender())) {
050: matching = new ArrayList();
051: for (Iterator i = mail.getRecipients().iterator(); i
052: .hasNext();) {
053: MailAddress recipient = (MailAddress) i.next();
054: if (isRecipientChecked(recipient)
055: && isOverQuota(recipient, mail)) {
056: matching.add(recipient);
057: }
058: }
059: }
060: return matching;
061: }
062:
063: /**
064: * Does the quota check.
065: * Checks if {@link #getQuota} < {@link #getUsed} for a recipient.
066: * Catches any throwable returning false, and so should any override do.
067: *
068: * @param address the recipient addresss to check
069: * @param mail the mail involved in the check
070: * @return true if over quota
071: */
072: protected boolean isOverQuota(MailAddress address, Mail mail) {
073: String user = address.getUser();
074: try {
075: boolean over = getQuota(address, mail) < getUsed(address,
076: mail);
077: if (over)
078: log(address + " is over quota.");
079: return over;
080: } catch (Throwable e) {
081: log("Exception checking quota for: " + address, e);
082: return false;
083: }
084: }
085:
086: /**
087: * Checks the sender.
088: * The default behaviour is to check that the sender <I>is not</I> null nor the local postmaster.
089: * If a subclass overrides this method it should "and" <CODE>super.isSenderChecked</CODE>
090: * to its check.
091: *
092: * @param sender the sender to check
093: */
094: protected boolean isSenderChecked(MailAddress sender)
095: throws MessagingException {
096: return !(sender == null || getMailetContext().getPostmaster()
097: .equals(sender));
098: }
099:
100: /**
101: * Checks the recipient.
102: * The default behaviour is to check that the recipient <I>is not</I> the local postmaster.
103: * If a subclass overrides this method it should "and" <CODE>super.isRecipientChecked</CODE>
104: * to its check.
105: *
106: * @param recipient the recipient to check
107: */
108: protected boolean isRecipientChecked(MailAddress recipient)
109: throws MessagingException {
110: return !(getMailetContext().getPostmaster().equals(recipient));
111: }
112:
113: /**
114: * Gets the quota to check against.
115: *
116: * @param address the address holding the quota if applicable
117: * @param mail the mail involved if needed
118: */
119: abstract protected long getQuota(MailAddress address, Mail mail)
120: throws MessagingException;
121:
122: /**
123: * Gets the used amount to check against the quota.
124: *
125: * @param address the address involved
126: * @param mail the mail involved if needed
127: */
128: abstract protected long getUsed(MailAddress address, Mail mail)
129: throws MessagingException;
130:
131: /**
132: * Utility method that parses an amount string.
133: * You can use 'k' and 'm' as optional postfixes to the amount (both upper and lowercase).
134: * In other words, "1m" is the same as writing "1024k", which is the same as
135: * "1048576".
136: *
137: * @param amount the amount string to parse
138: */
139: protected long parseQuota(String amount) throws MessagingException {
140: long quota;
141: try {
142: if (amount.endsWith("k")) {
143: amount = amount.substring(0, amount.length() - 1);
144: quota = Long.parseLong(amount) * 1024;
145: } else if (amount.endsWith("m")) {
146: amount = amount.substring(0, amount.length() - 1);
147: quota = Long.parseLong(amount) * 1024 * 1024;
148: } else {
149: quota = Long.parseLong(amount);
150: }
151: return quota;
152: } catch (Exception e) {
153: throw new MessagingException("Exception parsing quota", e);
154: }
155: }
156: }
|