001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.component.mail; 018 019 import javax.mail.Flags; 020 import javax.mail.Folder; 021 import javax.mail.FolderNotFoundException; 022 import javax.mail.Message; 023 import javax.mail.MessagingException; 024 import javax.mail.Store; 025 import javax.mail.search.FlagTerm; 026 027 import org.apache.camel.Processor; 028 import org.apache.camel.impl.ScheduledPollConsumer; 029 import org.apache.commons.logging.Log; 030 import org.apache.commons.logging.LogFactory; 031 import org.springframework.mail.javamail.JavaMailSenderImpl; 032 033 /** 034 * A {@link org.apache.camel.Consumer Consumer} which consumes messages from JavaMail using a 035 * {@link javax.mail.Transport Transport} and dispatches them to the {@link Processor} 036 * 037 * @version $Revision: 884527 $ 038 */ 039 public class MailConsumer extends ScheduledPollConsumer<MailExchange> { 040 public static final long DEFAULT_CONSUMER_DELAY = 60 * 1000L; 041 private static final transient Log LOG = LogFactory.getLog(MailConsumer.class); 042 043 private final MailEndpoint endpoint; 044 private final JavaMailSenderImpl sender; 045 private Folder folder; 046 private Store store; 047 048 public MailConsumer(MailEndpoint endpoint, Processor processor, JavaMailSenderImpl sender) { 049 super(endpoint, processor); 050 this.endpoint = endpoint; 051 this.sender = sender; 052 } 053 054 @Override 055 protected void doStart() throws Exception { 056 super.doStart(); 057 } 058 059 @Override 060 protected void doStop() throws Exception { 061 if (folder != null && folder.isOpen()) { 062 folder.close(true); 063 } 064 if (store != null && store.isConnected()) { 065 store.close(); 066 } 067 068 super.doStop(); 069 } 070 071 protected void poll() throws Exception { 072 ensureIsConnected(); 073 074 if (store == null || folder == null) { 075 throw new IllegalStateException("MailConsumer did not connect properly to the MailStore: " 076 + endpoint.getConfiguration().getMailStoreLogInformation()); 077 } 078 079 if (LOG.isDebugEnabled()) { 080 LOG.debug("Polling mailfolder: " + endpoint.getConfiguration().getMailStoreLogInformation()); 081 } 082 083 if (endpoint.getConfiguration().getFetchSize() == 0) { 084 LOG.warn("Fetch size is 0 meaning the configuration is set to poll no new messages at all. Camel will skip this poll."); 085 return; 086 } 087 088 // ensure folder is open 089 if (!folder.isOpen()) { 090 folder.open(Folder.READ_WRITE); 091 } 092 093 try { 094 int count = folder.getMessageCount(); 095 if (count > 0) { 096 Message[] messages; 097 098 // should we process all messages or only unseen messages 099 if (endpoint.getConfiguration().isProcessOnlyUnseenMessages()) { 100 messages = folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)); 101 } else { 102 messages = folder.getMessages(); 103 } 104 105 processMessages(messages); 106 107 } else if (count == -1) { 108 throw new MessagingException("Folder: " + folder.getFullName() + " is closed"); 109 } 110 } catch (Exception e) { 111 handleException(e); 112 } finally { 113 // need to ensure we release resources 114 try { 115 if (folder.isOpen()) { 116 folder.close(true); 117 } 118 } catch (MessagingException e) { 119 // some mail servers will lock the folder so we ignore in this case (CAMEL-1263) 120 LOG.debug("Could not close mailbox folder: " + folder.getName(), e); 121 } 122 } 123 } 124 125 protected void ensureIsConnected() throws MessagingException { 126 MailConfiguration config = endpoint.getConfiguration(); 127 128 boolean connected = false; 129 try { 130 if (store != null && store.isConnected()) { 131 connected = true; 132 } 133 } catch (Exception e) { 134 LOG.debug("Exception while testing for is connected to MailStore: " 135 + endpoint.getConfiguration().getMailStoreLogInformation() 136 + ". Caused by: " + e.getMessage(), e); 137 } 138 139 if (!connected) { 140 // ensure resources get recreated on reconnection 141 store = null; 142 folder = null; 143 144 if (LOG.isDebugEnabled()) { 145 LOG.debug("Connecting to MailStore: " + endpoint.getConfiguration().getMailStoreLogInformation()); 146 } 147 store = sender.getSession().getStore(config.getProtocol()); 148 store.connect(config.getHost(), config.getPort(), config.getUsername(), config.getPassword()); 149 } 150 151 if (folder == null) { 152 if (LOG.isDebugEnabled()) { 153 LOG.debug("Getting folder " + config.getFolderName()); 154 } 155 folder = store.getFolder(config.getFolderName()); 156 if (folder == null || !folder.exists()) { 157 throw new FolderNotFoundException(folder, "Folder not found or invalid: " + config.getFolderName()); 158 } 159 } 160 } 161 162 /** 163 * Process all the messages 164 */ 165 protected void processMessages(Message[] messages) throws Exception { 166 int fetchSize = endpoint.getConfiguration().getFetchSize(); 167 int count = fetchSize == -1 ? messages.length : Math.min(fetchSize, messages.length); 168 169 if (LOG.isDebugEnabled()) { 170 LOG.debug("Fetching " + count + " messages. Total " + messages.length + " messages."); 171 } 172 173 for (int i = 0; i < count; i++) { 174 Message message = messages[i]; 175 if (!message.getFlags().contains(Flags.Flag.DELETED)) { 176 177 MailExchange exchange = endpoint.createExchange(message); 178 process(exchange); 179 180 if (!exchange.isFailed()) { 181 processCommit(exchange); 182 } else { 183 processRollback(exchange); 184 } 185 } else { 186 if (LOG.isDebugEnabled()) { 187 LOG.debug("Skipping message as it was flagged as deleted: " + MailUtils.dumpMessage(message)); 188 } 189 } 190 } 191 } 192 193 /** 194 * Strategy to process the mail message. 195 */ 196 protected void process(MailExchange exchange) throws Exception { 197 if (LOG.isDebugEnabled()) { 198 LOG.debug("Processing message: " + MailUtils.dumpMessage(exchange.getIn().getMessage())); 199 } 200 getProcessor().process(exchange); 201 } 202 203 /** 204 * Strategy to flag the message after being processed. 205 */ 206 protected void processCommit(MailExchange exchange) throws MessagingException { 207 Message message = exchange.getIn().getMessage(); 208 209 if (endpoint.getConfiguration().isDeleteProcessedMessages()) { 210 LOG.debug("Exchange processed, so flagging message as DELETED"); 211 message.setFlag(Flags.Flag.DELETED, true); 212 } else { 213 LOG.debug("Exchange processed, so flagging message as SEEN"); 214 message.setFlag(Flags.Flag.SEEN, true); 215 } 216 } 217 218 /** 219 * Strategy when processing the exchange failed. 220 */ 221 protected void processRollback(MailExchange exchange) throws MessagingException { 222 LOG.warn("Exchange failed, so rolling back message status: " + exchange); 223 } 224 225 }