View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.portal.service.mail;
35  
36  import fr.paris.lutece.portal.service.daemon.AppDaemonService;
37  import fr.paris.lutece.portal.service.daemon.Daemon;
38  import fr.paris.lutece.portal.service.util.AppLogService;
39  import fr.paris.lutece.portal.service.util.AppPropertiesService;
40  
41  import java.util.Date;
42  import java.util.List;
43  import java.util.concurrent.TimeUnit;
44  
45  import javax.mail.MessagingException;
46  import javax.mail.NoSuchProviderException;
47  import javax.mail.SendFailedException;
48  import javax.mail.Session;
49  import javax.mail.Transport;
50  import javax.mail.internet.AddressException;
51  
52  import org.apache.logging.log4j.LogManager;
53  import org.apache.logging.log4j.Logger;
54  
55  /**
56   * MailSender Daemon
57   */
58  public class MailSenderDaemon extends Daemon
59  {
60      protected static final String DAEMON_ID = "mailSender";
61  
62      private static final String MESSAGE_ERROR_MAIL = "MailService - Error sending mail : ";
63      private static final String MESSAGE_STATUS_FAILED = " - Status [ Failed ] : ";
64      private static final String MESSAGE_ERROR_MAIL_MESSAGING = "MailService - Error sending mail (MessagingException): ";
65      private static final String PROPERTY_MAIL_HOST = "mail.server";
66      private static final String PROPERTY_MAIL_PORT = "mail.server.port";
67      private static final String PROPERTY_MAIL_DEAMON_WAITTIME = "mail.daemon.waittime";
68      private static final String PROPERTY_MAIL_DEAMON_COUNT = "mail.daemon.count";
69      private static final String PROPERTY_MAIL_USERNAME = "mail.username";
70      private static final String PROPERTY_MAIL_PASSWORD = "mail.password";
71      private static final String PROPERTY_MAIL_DAEMON_RETRYONERROR_WAITTIME = "mail.daemon.retryonerror.waittime";
72      private static final String PROPERTY_MAIL_DAEMON_RETRYONERROR_WAITTIME_UNIT = "mail.daemon.retryonerror.waittime.unit";
73      private static final int DEFAULT_SMTP_PORT = 25;
74  
75      /**
76       * {@inheritDoc}
77       */
78      @Override
79      public synchronized void run( )
80      {
81          Logger logger = LogManager.getLogger( "lutece.mail" );
82  
83          String strHost = AppPropertiesService.getProperty( PROPERTY_MAIL_HOST );
84          String strUsername = AppPropertiesService.getProperty( PROPERTY_MAIL_USERNAME, null );
85          String strPassword = AppPropertiesService.getProperty( PROPERTY_MAIL_PASSWORD, null );
86          int nStmpPort = AppPropertiesService.getPropertyInt( PROPERTY_MAIL_PORT, DEFAULT_SMTP_PORT );
87  
88          // Initializes a mail session with the SMTP server
89          StringBuilder sbLogs = new StringBuilder( );
90          IMailQueue queue = MailService.getQueue( );
91  
92          if ( queue.size( ) != 0 )
93          {
94              sbLogs.append( new Date( ).toString( ) );
95  
96              Session session = MailUtil.getMailSession( strHost, nStmpPort, strUsername, strPassword );
97              Transport transportSmtp = null;
98  
99              try
100             {
101                 transportSmtp = MailUtil.getTransport( session );
102             }
103             catch( NoSuchProviderException e )
104             {
105                 AppLogService.error( e.getMessage( ), e );
106             }
107 
108             if ( transportSmtp != null )
109             {
110                 try
111                 {
112                     transportSmtp.connect( strHost, nStmpPort, strUsername, strPassword );
113 
114                     sendMails( transportSmtp, session, queue, logger, sbLogs );
115 
116                     transportSmtp.close( );
117                 }
118                 catch( MessagingException e )
119                 {
120                     sbLogs.append( MESSAGE_ERROR_MAIL_MESSAGING );
121                     sbLogs.append( e.getMessage( ) );
122                     AppLogService.error( "{} {} ", MESSAGE_ERROR_MAIL_MESSAGING, e.getMessage( ), e );
123                 }
124                 catch( Exception e )
125                 {
126                     sbLogs.append( MESSAGE_ERROR_MAIL );
127                     sbLogs.append( e.getMessage( ) );
128                     AppLogService.error( "{} {} ", MESSAGE_ERROR_MAIL, e.getMessage( ), e );
129                 }
130             }
131 
132             // reset all resource stored in MailAttachmentCacheService
133             MailAttachmentCacheService.getInstance( ).resetCache( );
134             setLastRunLogs( sbLogs.toString( ) );
135         }
136         else
137         {
138             sbLogs.append( "\r\nNo mail to send " );
139             sbLogs.append( new Date( ).toString( ) );
140             logger.debug( sbLogs.toString( ) );
141         }
142     }
143 
144     private void sendMails( Transport transportSmtp, Session session, IMailQueue queue, Logger logger, StringBuilder sbLogs ) throws MessagingException
145     {
146         int nWaitTime = AppPropertiesService.getPropertyInt( PROPERTY_MAIL_DEAMON_WAITTIME, 1 );
147         int nCount = AppPropertiesService.getPropertyInt( PROPERTY_MAIL_DEAMON_COUNT, 1000 );
148         long nRetryWaitTime = AppPropertiesService.getPropertyLong( PROPERTY_MAIL_DAEMON_RETRYONERROR_WAITTIME, 60L );
149         TimeUnit retryWaitTimeUnit = TimeUnit.valueOf( AppPropertiesService.getProperty( PROPERTY_MAIL_DAEMON_RETRYONERROR_WAITTIME_UNIT, "SECONDS" ) );
150 
151         MailItem mail = queue.consume( );
152         int count = 0;
153 
154         while ( mail != null )
155         {
156             try
157             {
158                 if ( mail.isUniqueRecipientTo( ) )
159                 {
160                     List<String> listAdressTo = MailUtil.getAllStringAdressOfRecipients( mail.getRecipientsTo( ) );
161 
162                     for ( String strAdressTo : listAdressTo )
163                     {
164                         StringBuilder sbLogsLine = new StringBuilder( );
165                         // just one recipient by mail
166                         mail.setRecipientsTo( strAdressTo );
167                         sendMail( mail, transportSmtp, session, sbLogsLine );
168                         logger.info( sbLogsLine.toString( ) );
169                         sbLogs.append( "\r\n" );
170                         sbLogs.append( sbLogsLine );
171                     }
172                 }
173                 else
174                 {
175                     StringBuilder sbLogsLine = new StringBuilder( );
176                     sendMail( mail, transportSmtp, session, sbLogsLine );
177                     logger.info( sbLogsLine.toString( ) );
178                     sbLogs.append( "\r\n" );
179                     sbLogs.append( sbLogsLine );
180                 }
181             }
182             catch( MessagingException e )
183             {
184                 // if the connection is dead or not in the connected state
185                 // we put the mail in the queue before end process
186                 queue.send( mail );
187                 AppLogService.error( "Error while sending a message. Will schedule a retry", e );
188                 AppDaemonService.signalDaemon( DAEMON_ID, nRetryWaitTime, retryWaitTimeUnit );
189                 break;
190             }
191 
192             mail = queue.consume( );
193             // Tempo
194             count++;
195 
196             if ( ( count % nCount ) == 0 )
197             {
198                 transportSmtp.close( );
199                 AppDaemonService.signalDaemon( DAEMON_ID, nWaitTime, TimeUnit.MILLISECONDS );
200                 break;
201             }
202         }
203     }
204 
205     /**
206      * send mail
207      * 
208      * @param mail
209      *            the mail item
210      * @param transportSmtp
211      *            the smtp transport
212      * @param session
213      *            the session smtp
214      * @param sbLogsLine
215      *            the log line
216      * @throws MessagingException
217      *             See {@link MessagingException}
218      */
219     private void sendMail( MailItem mail, Transport transportSmtp, Session session, StringBuilder sbLogsLine ) throws MessagingException
220     {
221         try
222         {
223             sbLogsLine.append( " - To " );
224             sbLogsLine.append( ( ( mail.getRecipientsTo( ) != null ) ? mail.getRecipientsTo( ) : "" ) );
225             sbLogsLine.append( " - Cc " );
226             sbLogsLine.append( ( mail.getRecipientsCc( ) != null ) ? mail.getRecipientsCc( ) : "" );
227             sbLogsLine.append( " - Bcc " );
228             sbLogsLine.append( ( mail.getRecipientsBcc( ) != null ) ? mail.getRecipientsBcc( ) : "" );
229             sbLogsLine.append( " - Subject : " );
230             sbLogsLine.append( mail.getSubject( ) );
231 
232             switch( mail.getFormat( ) )
233             {
234                 case MailItem.FORMAT_HTML:
235                     MailUtil.sendMessageHtml( mail, transportSmtp, session );
236                     break;
237                 case MailItem.FORMAT_TEXT:
238                     MailUtil.sendMessageText( mail, transportSmtp, session );
239                     break;
240                 case MailItem.FORMAT_MULTIPART_HTML:
241                     MailUtil.sendMultipartMessageHtml( mail, transportSmtp, session );
242                     break;
243                 case MailItem.FORMAT_MULTIPART_TEXT:
244                     MailUtil.sendMultipartMessageText( mail, transportSmtp, session );
245                     break;
246                 case MailItem.FORMAT_CALENDAR:
247                     MailUtil.sendMessageCalendar( mail, transportSmtp, session );
248                     break;
249                 default:
250                     break;
251             }
252 
253             sbLogsLine.append( " - Status [ OK ]" );
254         }
255         catch( SendFailedException | AddressException e )
256         {
257             // a wrongly formatted address is encountered in the list of recipients
258             sbLogsLine.append( MESSAGE_STATUS_FAILED );
259             sbLogsLine.append( e.getMessage( ) );
260             AppLogService.error( "{} {} ", MESSAGE_ERROR_MAIL, e.getMessage( ), e );
261         }
262         catch( MessagingException e )
263         {
264             // if the connection is dead or not in the connected state
265             // we put the mail in the queue before end process
266             sbLogsLine.append( MESSAGE_STATUS_FAILED );
267             sbLogsLine.append( e.getMessage( ) );
268             AppLogService.error( "{} {} ", MESSAGE_ERROR_MAIL, e.getMessage( ), e );
269             throw e;
270         }
271     }
272 }