Java tutorial
/* * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.common.postfix; import java.io.StringReader; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import mitm.common.util.Check; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang.mutable.MutableInt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Parses the output of the "postqueue -p" command. The output is not strictly defined so parsing it is * not based on a defined API and it's therefore possible that it will break with new versions of Postfix. * * @author Martijn Brinkers * */ public class PostfixQueueParser { private static final Logger logger = LoggerFactory.getLogger(PostfixQueueParser.class); /* * The following reg. expr. parts are used to build the queue item pattern */ private static String QUEUE_ID_PART = "(\\w+)([\\*!])?"; private static String SIZE_PART = "\\s+(\\d+)"; private static String DATE_PART = "\\s+(\\w+\\s+\\w+\\s+\\d+\\s+\\d+(?::\\d+){2})"; private static String SENDER_PART = "\\s+(\\S*)"; private static String FAILURE_PART = "\\s+(\\(.*\\)(?!\\)))?"; private static String RECIPIENT_PART = "\\s*(.+)\\s*"; protected static Pattern QUEUE_PATTERN = Pattern .compile(QUEUE_ID_PART + SIZE_PART + DATE_PART + SENDER_PART + FAILURE_PART + RECIPIENT_PART); /* * The items from the queue */ private List<PostfixQueueItem> queueItems; /* * Pattern used to match the lines we are interested in. If null all lines will match */ private final Pattern searchPattern; private interface LineHandler { /* * called when a new line is called. Return false if parsing must be stopped */ public boolean lineFound(String line); } private class ParseQueueEntryLineHandler implements LineHandler { /* * The index of the first item */ private final int startIndex; /* * The max items to return */ private final int maxItems; /* * The current index */ int index; public ParseQueueEntryLineHandler(int startIndex, int maxItems) { this.startIndex = startIndex; this.maxItems = maxItems; } @Override public boolean lineFound(String line) { boolean keepParsing = true; if (index >= startIndex) { Matcher matcher = QUEUE_PATTERN.matcher(line); PostfixQueueItem newItem; if (matcher.matches() && matcher.groupCount() == 7) { String queueID = matcher.group(1); PostfixQueueStatus status = PostfixQueueStatus.fromString(matcher.group(2)); int messageSize = NumberUtils.toInt(matcher.group(3)); String deliveryDate = matcher.group(4); String sender = matcher.group(5); String failure = matcher.group(6); List<String> recipients = Arrays.asList(StringUtils.split(matcher.group(7))); newItem = new PostfixQueueItem(queueID, status, messageSize, deliveryDate, sender, failure, recipients); } else { logger.warn("Mail queue line is not recognized: " + line); /* * Because we did not recognize the line we do not know how to handle it. To make sure the user * sees at least the complete line we will add the line to the failure and set all other to null */ newItem = new PostfixQueueItem(null, null, -1, null, null, line, null); } queueItems.add(newItem); } index++; if ((index - startIndex) >= maxItems) { keepParsing = false; } return keepParsing; } }; public PostfixQueueParser(Pattern searchPattern) { this.searchPattern = searchPattern; } public PostfixQueueParser() { this(null); } private void parse(String queue, LineHandler lineHandler) { Check.notNull(lineHandler, "lineHandler"); StringReader reader = new StringReader(queue); try { LineIterator iterator = IOUtils.lineIterator(reader); /* * If the mail queue is empty the first line is "Mail queue is empty". If the mail queue is * not empty the first line should be the header. We should therefore skip the first line */ if (iterator.hasNext()) { iterator.next(); } while (iterator.hasNext()) { String line = iterator.nextLine(); if (line.startsWith("--")) { /* * The last line starts with -- so we are finished */ break; } /* * We need to collect all lines that belong to one queue item. Queue items use multiple lines * which are separated by an empty line */ while (iterator.hasNext()) { String otherLine = iterator.nextLine(); if (otherLine.length() == 0) { break; } line = line + " " + otherLine; } boolean match = true; if (searchPattern != null) { Matcher matcher = searchPattern.matcher(line); if (!matcher.find()) { match = false; } } if (match && !lineHandler.lineFound(line)) { break; } } } finally { IOUtils.closeQuietly(reader); } } /** * Creates a list of QueueItem's by parsing the raw postqueue output. */ public List<PostfixQueueItem> parseQueue(String queue, final int startIndex, final int maxItems) { queueItems = new LinkedList<PostfixQueueItem>(); parse(queue, new ParseQueueEntryLineHandler(startIndex, maxItems)); return queueItems; } /** * Returns the number of items in the Postfix queue. */ public int getQueueLength(String queue) { final MutableInt length = new MutableInt(0); LineHandler lineHandler = new LineHandler() { @Override public boolean lineFound(String line) { length.increment(); return true; } }; parse(queue, lineHandler); return length.intValue(); } /** * Returns the queue items. Returns null if no items have been parsed and an empty List if * there are no items. */ public List<PostfixQueueItem> getQueueItems() { return queueItems; } }