View Javadoc
1   /* 
2   Copyright (c) 2010, NHIN Direct Project
3   All rights reserved.
4   
5   Authors:
6      Greg Meyer      gm2552@cerner.com
7    
8   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
9   
10  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 
12  in the documentation and/or other materials provided with the distribution.  Neither the name of the The NHIN Direct Project (nhindirect.org). 
13  nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
14  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
15  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 
16  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
17  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
18  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
19  THE POSSIBILITY OF SUCH DAMAGE.
20  */
21  
22  package org.nhindirect.gateway.smtp.james.mailet;
23  
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  import javax.inject.Inject;
29  import javax.inject.Named;
30  import javax.mail.MessagingException;
31  import javax.mail.internet.MimeMessage;
32  
33  import org.apache.james.mailbox.MailboxManager;
34  import org.apache.james.metrics.api.MetricFactory;
35  import org.apache.james.transport.mailets.LocalDelivery;
36  import org.apache.james.user.api.UsersRepository;
37  import org.apache.mailet.Mail;
38  import org.nhindirect.common.mail.SMTPMailMessage;
39  import org.nhindirect.common.options.OptionsManager;
40  import org.nhindirect.common.tx.TxUtil;
41  import org.nhindirect.common.tx.model.Tx;
42  import org.nhindirect.common.tx.model.TxMessageType;
43  import org.nhindirect.gateway.GatewayConfiguration;
44  import org.nhindirect.gateway.smtp.NotificationProducer;
45  import org.nhindirect.gateway.smtp.NotificationSettings;
46  import org.nhindirect.gateway.smtp.ReliableDispatchedNotificationProducer;
47  import org.nhindirect.gateway.smtp.dsn.DSNCreator;
48  import org.nhindirect.gateway.smtp.dsn.impl.FailedDeliveryDSNCreator;
49  import org.nhindirect.gateway.util.MessageUtils;
50  import org.nhindirect.stagent.NHINDAddress;
51  import org.nhindirect.stagent.NHINDAddressCollection;
52  import org.nhindirect.stagent.mail.Message;
53  import org.nhindirect.stagent.mail.notifications.NotificationMessage;
54  import org.slf4j.Logger;
55  import org.slf4j.LoggerFactory;
56  
57  /**
58   * This mailet override the built in Apache James LocalDelivery mailet and sends an MDN dispatched message on successful delivery to a local mailbox
59   * if the message request timely and reliable message delivery.
60   * In addition, it also sends a DSN failure message if the message cannot be placed into the local mailbox.
61   * @author Greg Meyer
62   * @since 2.0
63   */
64  public class TimelyAndReliableLocalDelivery extends AbstractNotificationAwareMailet
65  {
66  	private static final Logger LOGGER = LoggerFactory.getLogger(TimelyAndReliableLocalDelivery.class);
67  
68  	protected static final String DISPATCHED_MDN_DELAY = "DispatchedMDNDelay";
69  	
70  	private UsersRepository usersRepository;
71  
72  	private MailboxManager mailboxManager;
73  
74  	private MetricFactory metricFactory;
75  	
76  	protected LocalDelivery localDeliveryMailet;
77  		
78  	protected NotificationProducer notificationProducer;
79  	
80  	protected int dispatchedMDNDelay;
81  	
82  	static
83  	{		
84  		initJVMParams();
85  	}
86  	
87  	private synchronized static void initJVMParams()
88  	{
89  		/*
90  		 * Mailet configuration parameters
91  		 */
92  		final Map<String, String> JVM_PARAMS = new HashMap<String, String>();
93  		JVM_PARAMS.put(DISPATCHED_MDN_DELAY, "org.nhindirect.gateway.smtp.james.mailet.DispatchedMDNDelay");
94  		
95  		OptionsManager.addInitParameters(JVM_PARAMS);
96  	}	
97  	
98      @Inject
99      public TimelyAndReliableLocalDelivery(UsersRepository usersRepository, @Named("mailboxmanager") MailboxManager mailboxManager,
100                          MetricFactory metricFactory) 
101     {
102         this.metricFactory = metricFactory;
103         this.usersRepository = usersRepository;
104         this.mailboxManager = mailboxManager;
105 		// create an instance of the local delivery if we can
106 		this.localDeliveryMailet = createLocalDeliveryClass();
107     }	
108 	
109 	/**
110 	 * {@inheritDoc}
111 	 */
112 	public void init() throws MessagingException
113 	{
114 		super.init();
115 		
116 
117 		
118 		try
119 		{
120 			final String sDispatchedDelay =  GatewayConfiguration.getConfigurationParam(DISPATCHED_MDN_DELAY,
121 					this, ctx, "0"); 
122 			
123 			try
124 			{
125 				dispatchedMDNDelay = Integer.valueOf(sDispatchedDelay).intValue();
126 			}
127 			catch (NumberFormatException e)
128 			{
129 				// in case of parsing exceptions
130 				dispatchedMDNDelay = 0;
131 			}
132 			
133 			localDeliveryMailet.init(this.getMailetConfig());
134 
135 		}
136 		catch (Exception e)
137 		{
138 			throw new MessagingException("Failed to initialize TimelyAndReliableLocalDelivery.", e);
139 		}
140 		
141 		notificationProducer = new ReliableDispatchedNotificationProducer(new NotificationSettings(true, "Local Direct Delivery Agent", "Your message was successfully dispatched."));
142 	}
143 	
144 	
145 	protected LocalDelivery createLocalDeliveryClass()
146 	{
147 		final LocalDelivery retVal = new LocalDelivery(usersRepository, mailboxManager, metricFactory);
148 		
149 		return retVal;
150 	}
151 	
152 	/**
153 	 * {@inheritDoc}
154 	 */
155 	@Override
156 	public void service(Mail mail) throws MessagingException 
157 	{
158 		LOGGER.debug("Calling timely and reliable service method.");
159 		
160 		boolean deliverySuccessful = false;
161 		
162 		final MimeMessage msg = mail.getMessage();
163 		final boolean isReliableAndTimely = TxUtil.isReliableAndTimelyRequested(msg);
164 		
165 		final SMTPMailMessage smtpMailMessage = mailToSMTPMailMessage(mail);
166 		
167 		final NHINDAddressCollection recipients = MessageUtils.getMailRecipients(smtpMailMessage);
168 		
169 		final NHINDAddress sender = MessageUtils.getMailSender(smtpMailMessage);
170 		
171 		
172 		try
173 		{
174 			localDeliveryMailet.service(mail);
175 			deliverySuccessful = true;
176 		}
177 		catch (Exception e)
178 		{
179 			LOGGER.error("Failed to invoke service method.", e);
180 		}
181 		
182 		final Tx txToTrack = this.getTxToTrack(msg, sender, recipients);
183 		
184 		if (deliverySuccessful)
185 		{	
186 			if (isReliableAndTimely && txToTrack.getMsgType() == TxMessageType.IMF)
187 			{
188 
189 				// send back an MDN dispatched message
190 				final Collection<NotificationMessage> notifications = 
191 						notificationProducer.produce(new Message(msg), recipients.toInternetAddressCollection());
192 				if (notifications != null && notifications.size() > 0)
193 				{
194 					LOGGER.debug("Sending MDN \"dispatched\" messages");
195 					// create a message for each notification and put it on James "stack"
196 					for (NotificationMessage message : notifications)
197 					{
198 						try
199 						{
200 							message.saveChanges();
201 							
202 							if (dispatchedMDNDelay > 0)
203 								Thread.sleep(dispatchedMDNDelay);
204 							
205 							getMailetContext().sendMail(message);
206 						}
207 						///CLOVER:OFF
208 						catch (Throwable t)
209 						{
210 							// don't kill the process if this fails
211 							LOGGER.error("Error sending MDN dispatched message.", t);
212 						}
213 						///CLOVER:ON
214 					}
215 				}
216 			}
217 		}
218 		else
219 		{
220 			// create a DSN message regarless if timely and reliable was requested
221 			if (txToTrack != null && txToTrack.getMsgType() == TxMessageType.IMF)
222 				this.sendDSN(txToTrack, recipients, false);
223 		}
224 		
225 		LOGGER.debug("Exiting timely and reliable service method.");
226 	}
227 
228 	/**
229 	 * {@inheritDoc}
230 	 */
231 	@Override
232 	protected DSNCreator createDSNGenerator() 
233 	{
234 		return new FailedDeliveryDSNCreator(this);
235 	}
236 }