View Javadoc
1   package org.apache.maven.plugin.announcement;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.IOException;
26  import java.io.InputStreamReader;
27  import java.io.UnsupportedEncodingException;
28  import java.util.List;
29  
30  import javax.mail.internet.AddressException;
31  import javax.mail.internet.InternetAddress;
32  
33  import org.apache.maven.model.Developer;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.announcement.mailsender.ProjectJavamailMailSender;
36  import org.apache.maven.plugins.annotations.Execute;
37  import org.apache.maven.plugins.annotations.Mojo;
38  import org.apache.maven.plugins.annotations.Parameter;
39  import org.apache.maven.project.MavenProject;
40  import org.codehaus.plexus.logging.Logger;
41  import org.codehaus.plexus.logging.console.ConsoleLogger;
42  import org.codehaus.plexus.mailsender.MailMessage;
43  import org.codehaus.plexus.mailsender.MailSenderException;
44  import org.codehaus.plexus.util.IOUtil;
45  import org.codehaus.plexus.util.ReaderFactory;
46  import org.codehaus.plexus.util.StringUtils;
47  
48  /**
49   * Goal which sends an announcement through email.
50   *
51   * @author aramirez@exist.com
52   * @version $Id: AnnouncementMailMojo.java 1685894 2015-06-16 19:29:09Z khmarbaise $
53   * @since 2.0-beta-2
54   */
55  @Mojo( name = "announcement-mail", threadSafe = true )
56  @Execute( goal = "announcement-generate" )
57  public class AnnouncementMailMojo
58      extends AbstractAnnouncementMojo
59  {
60      // =========================================
61      // announcement-mail goal fields
62      // =========================================
63  
64      /**
65       * Possible senders.
66       */
67      @Parameter( property = "project.developers", required = true, readonly = true )
68      private List from;
69  
70      /**
71       * The id of the developer sending the announcement mail. Only used if the <tt>mailSender</tt> attribute is not set.
72       * In this case, this should match the id of one of the developers in the pom. If a matching developer is not found,
73       * then the first developer in the pom will be used.
74       */
75      @Parameter( property = "changes.fromDeveloperId" )
76      private String fromDeveloperId;
77  
78      /**
79       * Mail content type to use.
80       *
81       * @since 2.1
82       */
83      @Parameter( defaultValue = "text/plain", required = true )
84      private String mailContentType;
85  
86      /**
87       * Defines the sender of the announcement email. This takes precedence over the list of developers specified in the
88       * POM. if the sender is not a member of the development team. Note that since this is a bean type, you cannot
89       * specify it from command level with
90       * 
91       * <pre>
92       * -D
93       * </pre>
94       * 
95       * . Use
96       * 
97       * <pre>
98       * -Dchanges.sender='Your Name &lt;you@domain>'
99       * </pre>
100      * 
101      * instead.
102      */
103     @Parameter( property = "changes.mailSender" )
104     private MailSender mailSender;
105 
106     /**
107      * Defines the sender of the announcement. This takes precedence over both ${changes.mailSender} and the list of
108      * developers in the POM.
109      * <p/>
110      * This parameter parses an email address in standard RFC822 format, e.g.
111      * 
112      * <pre>
113      * -Dchanges.sender='Your Name &lt;you@domain>'
114      * </pre>
115      * 
116      * .
117      *
118      * @since 2.7
119      */
120     @Parameter( property = "changes.sender" )
121     private String senderString;
122 
123     /**
124      * The password used to send the email.
125      */
126     @Parameter( property = "changes.password" )
127     private String password;
128 
129     /**
130      */
131     @Parameter( defaultValue = "${project}", readonly = true, required = true )
132     private MavenProject project;
133 
134     /**
135      * Smtp Server.
136      */
137     @Parameter( property = "changes.smtpHost", required = true )
138     private String smtpHost;
139 
140     /**
141      * Port.
142      */
143     @Parameter( property = "changes.smtpPort", defaultValue = "25", required = true )
144     private int smtpPort;
145 
146     /**
147      * If the email should be sent in SSL mode.
148      */
149     @Parameter( property = "changes.sslMode", defaultValue = "false" )
150     private boolean sslMode;
151 
152     /**
153      * If the option startTls should be used.
154      *
155      * @since 2.10
156      */
157     @Parameter( property = "changes.startTls", defaultValue = "false" )
158     private boolean startTls;
159 
160     /**
161      * Subject for the email.
162      */
163     // CHECKSTYLE_OFF: LineLength
164     @Parameter( property = "changes.subject", defaultValue = "[ANNOUNCEMENT] - ${project.name} ${project.version} released", required = true )
165     private String subject;
166     // CHECKSTYLE_ON: LineLength
167 
168     /**
169      * The file that contains the generated announcement.
170      *
171      * @since 2.10
172      */
173     @Parameter( property = "changes.announcementFile", defaultValue = "announcement.vm", required = true )
174     private String announcementFile;
175 
176     /**
177      * Directory where the generated announcement file exists.
178      *
179      * @since 2.10
180      */
181     @Parameter( defaultValue = "${project.build.directory}/announcement", required = true )
182     private File announcementDirectory;
183 
184     /**
185      * The encoding used in the announcement template.
186      *
187      * @since 2.10
188      */
189     @Parameter( property = "changes.templateEncoding", defaultValue = "${project.build.sourceEncoding}" )
190     private String templateEncoding;
191 
192     /**
193      * Directory which contains the template for announcement email.
194      *
195      * @deprecated Starting with version 2.10 this parameter is no longer used. You must use
196      *             {@link #announcementDirectory} instead.
197      */
198     @Parameter
199     private File templateOutputDirectory;
200 
201     /**
202      * Recipient email address.
203      */
204     @Parameter( required = true )
205     private List toAddresses;
206 
207     /**
208      * Recipient cc email address.
209      *
210      * @since 2.5
211      */
212     @Parameter
213     private List ccAddresses;
214 
215     /**
216      * Recipient bcc email address.
217      *
218      * @since 2.5
219      */
220     @Parameter
221     private List bccAddresses;
222 
223     /**
224      * The username used to send the email.
225      */
226     @Parameter( property = "changes.username" )
227     private String username;
228 
229     private ProjectJavamailMailSender mailer = new ProjectJavamailMailSender();
230 
231     public void execute()
232         throws MojoExecutionException
233     {
234         // Fail build fast if it is using deprecated parameters
235         if ( templateOutputDirectory != null )
236         {
237             throw new MojoExecutionException( "You are using the old parameter 'templateOutputDirectory'. "
238                 + "You must use 'announcementDirectory' instead." );
239         }
240 
241         // Run only at the execution root
242         if ( runOnlyAtExecutionRoot && !isThisTheExecutionRoot() )
243         {
244             getLog().info( "Skipping the announcement mail in this project because it's not the Execution Root" );
245         }
246         else
247         {
248             File file = new File( announcementDirectory, announcementFile );
249 
250             ConsoleLogger logger = new ConsoleLogger( Logger.LEVEL_INFO, "base" );
251 
252             if ( getLog().isDebugEnabled() )
253             {
254                 logger.setThreshold( Logger.LEVEL_DEBUG );
255             }
256 
257             mailer.enableLogging( logger );
258 
259             mailer.setSmtpHost( getSmtpHost() );
260 
261             mailer.setSmtpPort( getSmtpPort() );
262 
263             mailer.setSslMode( sslMode, startTls );
264 
265             if ( username != null )
266             {
267                 mailer.setUsername( username );
268             }
269 
270             if ( password != null )
271             {
272                 mailer.setPassword( password );
273             }
274 
275             mailer.initialize();
276 
277             if ( getLog().isDebugEnabled() )
278             {
279                 getLog().debug( "fromDeveloperId: " + getFromDeveloperId() );
280             }
281 
282             if ( file.isFile() )
283             {
284                 getLog().info( "Connecting to Host: " + getSmtpHost() + ":" + getSmtpPort() );
285 
286                 sendMessage();
287             }
288             else
289             {
290                 throw new MojoExecutionException( "Announcement file " + file + " not found..." );
291             }
292         }
293     }
294 
295     /**
296      * Send the email.
297      *
298      * @throws MojoExecutionException if the mail could not be sent
299      */
300     protected void sendMessage()
301         throws MojoExecutionException
302     {
303         File file = new File( announcementDirectory, announcementFile );
304         String email = "";
305         final MailSender ms = getActualMailSender();
306         final String fromName = ms.getName();
307         final String fromAddress = ms.getEmail();
308         if ( fromAddress == null || fromAddress.equals( "" ) )
309         {
310             throw new MojoExecutionException( "Invalid mail sender: name and email is mandatory (" + ms + ")." );
311         }
312         getLog().info( "Using this sender for email announcement: " + fromAddress + " < " + fromName + " > " );
313         try
314         {
315             MailMessage mailMsg = new MailMessage();
316             mailMsg.setSubject( getSubject() );
317             mailMsg.setContent( readAnnouncement( file ) );
318             mailMsg.setContentType( this.mailContentType );
319             mailMsg.setFrom( fromAddress, fromName );
320 
321             for ( Object o1 : getToAddresses() )
322             {
323                 email = o1.toString();
324                 getLog().info( "Sending mail to " + email + "..." );
325                 mailMsg.addTo( email, "" );
326             }
327 
328             if ( getCcAddresses() != null )
329             {
330                 for ( Object o : getCcAddresses() )
331                 {
332                     email = o.toString();
333                     getLog().info( "Sending cc mail to " + email + "..." );
334                     mailMsg.addCc( email, "" );
335                 }
336             }
337 
338             if ( getBccAddresses() != null )
339             {
340                 for ( Object o : getBccAddresses() )
341                 {
342                     email = o.toString();
343                     getLog().info( "Sending bcc mail to " + email + "..." );
344                     mailMsg.addBcc( email, "" );
345                 }
346             }
347 
348             mailer.send( mailMsg );
349             getLog().info( "Sent..." );
350         }
351         catch ( MailSenderException e )
352         {
353             throw new MojoExecutionException( "Failed to send email < " + email + " >", e );
354         }
355     }
356 
357     /**
358      * Read the content of the generated announcement file.
359      *
360      * @param file the file to be read
361      * @return Return the announcement text
362      * @throws MojoExecutionException if the file could not be found, or if the encoding is unsupported
363      */
364     protected String readAnnouncement( File file )
365         throws MojoExecutionException
366     {
367         InputStreamReader reader = null;
368         FileInputStream inputStream = null;
369         try
370         {
371             inputStream = new FileInputStream( file );
372 
373             if ( StringUtils.isEmpty( templateEncoding ) )
374             {
375                 templateEncoding = ReaderFactory.FILE_ENCODING;
376                 getLog().warn( "File encoding has not been set, using platform encoding '" + templateEncoding
377                     + "', i.e. build is platform dependent!" );
378             }
379 
380             reader = new InputStreamReader( inputStream, templateEncoding );
381             return IOUtil.toString( reader );
382         }
383         catch ( FileNotFoundException fnfe )
384         {
385             throw new MojoExecutionException( "File not found. " + file );
386         }
387         catch ( UnsupportedEncodingException uee )
388         {
389             throw new MojoExecutionException( "Unsupported encoding: '" + templateEncoding + "'" );
390         }
391         catch ( IOException ioe )
392         {
393             throw new MojoExecutionException( "Failed to read the announcement file.", ioe );
394         }
395         finally
396         {
397             IOUtil.close( inputStream );
398             IOUtil.close( reader );
399         }
400     }
401 
402     /**
403      * Returns the identify of the mail sender according to the plugin's configuration:
404      * <ul>
405      * <li>if the <tt>mailSender</tt> parameter is set, it is returned</li>
406      * <li>if no <tt>fromDeveloperId</tt> is set, the first developer in the list is returned</li>
407      * <li>if a <tt>fromDeveloperId</tt> is set, the developer with that id is returned</li>
408      * <li>if the developers list is empty or if the specified id does not exist, an exception is thrown</li>
409      * </ul>
410      *
411      * @return the mail sender to use
412      * @throws MojoExecutionException if the mail sender could not be retrieved
413      */
414     protected MailSender getActualMailSender()
415         throws MojoExecutionException
416     {
417         if ( senderString != null )
418         {
419             try
420             {
421                 InternetAddress ia = new InternetAddress( senderString, true );
422                 return new MailSender( ia.getPersonal(), ia.getAddress() );
423             }
424             catch ( AddressException e )
425             {
426                 throw new MojoExecutionException( "Invalid value for change.sender: ", e );
427             }
428         }
429         if ( mailSender != null && mailSender.getEmail() != null )
430         {
431             return mailSender;
432         }
433         else if ( from == null || from.isEmpty() )
434         {
435             throw new MojoExecutionException( "The <developers> section in your pom should not be empty. "
436                 + "Add a <developer> entry or set the mailSender parameter." );
437         }
438         else if ( fromDeveloperId == null )
439         {
440             final Developer dev = (Developer) from.get( 0 );
441             return new MailSender( dev.getName(), dev.getEmail() );
442         }
443         else
444         {
445             for ( Object aFrom : from )
446             {
447                 Developer developer = (Developer) aFrom;
448 
449                 if ( fromDeveloperId.equals( developer.getId() ) )
450                 {
451                     return new MailSender( developer.getName(), developer.getEmail() );
452                 }
453             }
454             throw new MojoExecutionException( "Missing developer with id '" + fromDeveloperId
455                 + "' in the <developers> section in your pom." );
456         }
457     }
458 
459     // ================================
460     // announcement-mail accessors
461     // ================================
462 
463     public List getBccAddresses()
464     {
465         return bccAddresses;
466     }
467 
468     public void setBccAddresses( List bccAddresses )
469     {
470         this.bccAddresses = bccAddresses;
471     }
472 
473     public List getCcAddresses()
474     {
475         return ccAddresses;
476     }
477 
478     public void setCcAddresses( List ccAddresses )
479     {
480         this.ccAddresses = ccAddresses;
481     }
482 
483     public List getFrom()
484     {
485         return from;
486     }
487 
488     public void setFrom( List from )
489     {
490         this.from = from;
491     }
492 
493     public String getFromDeveloperId()
494     {
495         return fromDeveloperId;
496     }
497 
498     public void setFromDeveloperId( String fromDeveloperId )
499     {
500         this.fromDeveloperId = fromDeveloperId;
501     }
502 
503     public MailSender getMailSender()
504     {
505         return mailSender;
506     }
507 
508     public void setMailSender( MailSender mailSender )
509     {
510         this.mailSender = mailSender;
511     }
512 
513     public String getPassword()
514     {
515         return password;
516     }
517 
518     public void setPassword( String password )
519     {
520         this.password = password;
521     }
522 
523     public MavenProject getProject()
524     {
525         return project;
526     }
527 
528     public void setProject( MavenProject project )
529     {
530         this.project = project;
531     }
532 
533     public String getSmtpHost()
534     {
535         return smtpHost;
536     }
537 
538     public void setSmtpHost( String smtpHost )
539     {
540         this.smtpHost = smtpHost;
541     }
542 
543     public int getSmtpPort()
544     {
545         return smtpPort;
546     }
547 
548     public void setSmtpPort( int smtpPort )
549     {
550         this.smtpPort = smtpPort;
551     }
552 
553     public boolean isSslMode()
554     {
555         return sslMode;
556     }
557 
558     public void setSslMode( boolean sslMode )
559     {
560         this.sslMode = sslMode;
561     }
562 
563     public boolean isStartTls()
564     {
565         return startTls;
566     }
567 
568     public void setStartTls( boolean startTls )
569     {
570         this.startTls = startTls;
571     }
572 
573     public String getSubject()
574     {
575         return subject;
576     }
577 
578     public void setSubject( String subject )
579     {
580         this.subject = subject;
581     }
582 
583     public String getAnnouncementFile()
584     {
585         return announcementFile;
586     }
587 
588     public void setAnnouncementFile( String announcementFile )
589     {
590         this.announcementFile = announcementFile;
591     }
592 
593     public File getAnnouncementDirectory()
594     {
595         return announcementDirectory;
596     }
597 
598     public void setAnnouncementDirectory( File announcementDirectory )
599     {
600         this.announcementDirectory = announcementDirectory;
601     }
602 
603     public List getToAddresses()
604     {
605         return toAddresses;
606     }
607 
608     public void setToAddresses( List toAddresses )
609     {
610         this.toAddresses = toAddresses;
611     }
612 
613     public String getUsername()
614     {
615         return username;
616     }
617 
618     public void setUsername( String username )
619     {
620         this.username = username;
621     }
622 }