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 }