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 (Mon, 08 Jan 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 }