001/*
002 *   Copyright (C) 2005 Christian Schulte <cs@schulte.it>
003 *   All rights reserved.
004 *
005 *   Redistribution and use in source and binary forms, with or without
006 *   modification, are permitted provided that the following conditions
007 *   are met:
008 *
009 *     o Redistributions of source code must retain the above copyright
010 *       notice, this list of conditions and the following disclaimer.
011 *
012 *     o Redistributions in binary form must reproduce the above copyright
013 *       notice, this list of conditions and the following disclaimer in
014 *       the documentation and/or other materials provided with the
015 *       distribution.
016 *
017 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 *   $JOMC: AbstractAttachMojo.java 5325 2016-09-01 00:33:38Z schulte $
029 *
030 */
031package org.jomc.mojo;
032
033import java.io.File;
034import java.io.FileInputStream;
035import java.io.FileOutputStream;
036import java.io.IOException;
037import java.io.InputStream;
038import java.io.OutputStream;
039import org.apache.maven.artifact.ArtifactUtils;
040import org.apache.maven.execution.MavenSession;
041import org.apache.maven.plugin.AbstractMojo;
042import org.apache.maven.plugin.MojoExecutionException;
043import org.apache.maven.plugin.MojoFailureException;
044import org.apache.maven.plugin.descriptor.MojoDescriptor;
045import org.apache.maven.plugins.annotations.Component;
046import org.apache.maven.plugins.annotations.Parameter;
047import org.apache.maven.project.MavenProject;
048import org.apache.maven.project.MavenProjectHelper;
049
050/**
051 * Base class for attaching artifacts to a project.
052 *
053 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
054 * @version $JOMC: AbstractAttachMojo.java 5325 2016-09-01 00:33:38Z schulte $
055 */
056public abstract class AbstractAttachMojo extends AbstractMojo
057{
058
059    /**
060     * Constant for the name of the tool backing the mojo.
061     */
062    private static final String TOOLNAME = "MavenProjectHelper";
063
064    /**
065     * Prefix prepended to log messages.
066     */
067    private static final String LOG_PREFIX = "[JOMC] ";
068
069    /**
070     * The Maven project of the instance.
071     */
072    @Parameter( name = "mavenProject",
073                defaultValue = "${project}",
074                readonly = true,
075                required = true )
076    private MavenProject mavenProject;
077
078    /**
079     * The Maven ProjectHelper of the instance.
080     */
081    @Component
082    private MavenProjectHelper mavenProjectHelper;
083
084    /**
085     * The Maven session of the instance.
086     *
087     * @since 1.1
088     */
089    @Parameter( name = "mavenSession",
090                defaultValue = "${session}",
091                readonly = true,
092                required = true )
093    private MavenSession mavenSession;
094
095    /**
096     * Directory holding the session related files of the project.
097     *
098     * @since 1.1
099     */
100    @Parameter( name = "sessionDirectory",
101                property = "jomc.sessionDirectory",
102                defaultValue = "${project.build.directory}/jomc-sessions" )
103    private String sessionDirectory;
104
105    /**
106     * Controls verbosity of the plugin.
107     *
108     * @since 1.1
109     */
110    @Parameter( name = "verbose",
111                property = "jomc.verbose",
112                defaultValue = "false" )
113    private boolean verbose;
114
115    /**
116     * Creates a new {@code AbstractAttachMojo} instance.
117     */
118    public AbstractAttachMojo()
119    {
120        super();
121    }
122
123    /**
124     * Gets the Maven project of the instance.
125     *
126     * @return The Maven project of the instance.
127     *
128     * @throws MojoExecutionException if getting the Maven project of the instance fails.
129     *
130     * @since 1.1
131     */
132    protected MavenProject getMavenProject() throws MojoExecutionException
133    {
134        return this.mavenProject;
135    }
136
137    /**
138     * Gets the Maven session of the instance.
139     *
140     * @return The Maven session of the instance.
141     *
142     * @throws MojoExecutionException if getting the Maven session of the instance fails.
143     *
144     * @since 1.1
145     */
146    protected MavenSession getMavenSession() throws MojoExecutionException
147    {
148        return this.mavenSession;
149    }
150
151    /**
152     * Gets the Maven project helper of the instance.
153     *
154     * @return The Maven project helper of the instance.
155     *
156     * @throws MojoExecutionException if getting the Maven project helper of the instance fails.
157     *
158     * @since 1.1
159     */
160    protected MavenProjectHelper getMavenProjectHelper() throws MojoExecutionException
161    {
162        return this.mavenProjectHelper;
163    }
164
165    /**
166     * Gets the directory holding the session related files of the project.
167     *
168     * @return The directory holding the session related files of the project.
169     *
170     * @throws MojoExecutionException if getting the directory fails.
171     *
172     * @since 1.1
173     */
174    protected File getSessionDirectory() throws MojoExecutionException
175    {
176        File directory = new File( this.sessionDirectory );
177
178        if ( !directory.isAbsolute() )
179        {
180            directory = new File( this.getMavenProject().getBasedir(), this.sessionDirectory );
181        }
182
183        return directory;
184    }
185
186    /**
187     * Gets a flag indicating verbose output is enabled.
188     *
189     * @return {@code true}, if verbose output is enabled; {@code false}, if information messages are suppressed.
190     *
191     * @throws MojoExecutionException if getting the flag fails.
192     *
193     * @since 1.1
194     */
195    protected final boolean isVerbose() throws MojoExecutionException
196    {
197        return this.verbose;
198    }
199
200    /**
201     * Sets the flag indicating verbose output is enabled.
202     *
203     * @param value {@code true}, to enable verbose output; {@code false}, to suppress information messages.
204     *
205     * @throws MojoExecutionException if setting the flag fails.
206     *
207     * @since 1.1
208     */
209    protected final void setVerbose( final boolean value ) throws MojoExecutionException
210    {
211        this.verbose = value;
212    }
213
214    /**
215     * Gets the file of the artifact to attach.
216     *
217     * @return The file of the artifact to attach.
218     */
219    protected abstract File getArtifactFile();
220
221    /**
222     * Gets the classifier of the artifact to attach.
223     *
224     * @return The classifier of the artifact to attach.
225     */
226    protected abstract String getArtifactClassifier();
227
228    /**
229     * Gets the type of the artifact to attach.
230     *
231     * @return The type of the artifact to attach.
232     */
233    protected abstract String getArtifactType();
234
235    /**
236     * Gets the execution strategy of the instance.
237     *
238     * @return The execution strategy of the instance.
239     *
240     * @since 1.1
241     */
242    protected abstract String getExecutionStrategy();
243
244    public final void execute() throws MojoExecutionException, MojoFailureException
245    {
246        final File attachment =
247            new File( this.getSessionDirectory(),
248                      ArtifactUtils.versionlessKey( this.getMavenProject().getArtifact() ).hashCode()
249                          + "-" + this.getArtifactClassifier()
250                          + "-" + this.getMavenSession().getStartTime().getTime()
251                          + "." + this.getArtifactType() );
252
253        try
254        {
255            if ( this.isVerbose() && this.getLog().isInfoEnabled() )
256            {
257                this.getLog().info( LOG_PREFIX + Messages.getMessage( "separator" ) );
258                this.getLog().info( LOG_PREFIX + Messages.getMessage( "title" ) );
259            }
260
261            if ( MojoDescriptor.MULTI_PASS_EXEC_STRATEGY.equals( this.getExecutionStrategy() )
262                     || !attachment.exists() )
263            {
264                if ( this.isVerbose() && this.getLog().isInfoEnabled() )
265                {
266                    this.getLog().info( LOG_PREFIX + Messages.getMessage( "separator" ) );
267                    this.getLog().info( LOG_PREFIX + Messages.getMessage(
268                        "processingProject", TOOLNAME, this.getMavenProject().getName() == null
269                                                           ? this.getMavenProject().getArtifactId()
270                                                           : this.getMavenProject().getName() ) );
271
272                }
273
274                if ( this.getArtifactFile().isFile() )
275                {
276                    if ( attachment.exists() && !attachment.delete() )
277                    {
278                        this.getLog().warn( LOG_PREFIX + Messages.getMessage(
279                            "failedDeletingFile", attachment.getAbsolutePath() ) );
280
281                    }
282                    if ( !attachment.getParentFile().exists() && !attachment.getParentFile().mkdirs() )
283                    {
284                        throw new MojoExecutionException( Messages.getMessage(
285                            "failedCreatingDirectory", attachment.getParentFile().getAbsolutePath() ) );
286
287                    }
288
289                    this.copyFile( this.getArtifactFile(), attachment );
290                    this.getMavenProjectHelper().attachArtifact( this.getMavenProject(), this.getArtifactType(),
291                                                                 this.getArtifactClassifier(), attachment );
292
293                    if ( this.isVerbose() && this.getLog().isInfoEnabled() )
294                    {
295                        this.getLog().info( LOG_PREFIX + Messages.getMessage(
296                            "creatingAttachment", this.getArtifactFile().getAbsolutePath(),
297                            this.getArtifactClassifier(), this.getArtifactType() ) );
298
299                        this.getLog().info( LOG_PREFIX + Messages.getMessage( "toolSuccess", TOOLNAME ) );
300                    }
301                }
302                else if ( this.getLog().isWarnEnabled() )
303                {
304                    this.getLog().warn( LOG_PREFIX + Messages.getMessage(
305                        "artifactFileNotFound", this.getArtifactFile().getAbsolutePath() ) );
306
307                }
308            }
309            else if ( this.isVerbose() && this.getLog().isInfoEnabled() )
310            {
311                this.getLog().info( LOG_PREFIX + Messages.getMessage( "executionSuppressed",
312                                                                      this.getExecutionStrategy() ) );
313
314            }
315        }
316        catch ( final IOException e )
317        {
318            final String message = Messages.getMessage( e );
319            throw new MojoExecutionException( Messages.getMessage(
320                "failedCopying", this.getArtifactFile().getAbsolutePath(), attachment.getAbsolutePath(),
321                message != null ? message : "" ), e );
322
323        }
324        finally
325        {
326            if ( this.isVerbose() && this.getLog().isInfoEnabled() )
327            {
328                this.getLog().info( LOG_PREFIX + Messages.getMessage( "separator" ) );
329            }
330        }
331    }
332
333    /**
334     * Copies a file.
335     *
336     * @param source The file to copy.
337     * @param target The file to copy the source file to.
338     *
339     * @throws IOException if copying fails.
340     *
341     * @since 1.10
342     */
343    protected final void copyFile( final File source, final File target ) throws IOException
344    {
345        InputStream in = null;
346        OutputStream out = null;
347        try
348        {
349            if ( !source.equals( target ) )
350            {
351                in = new FileInputStream( source );
352                out = new FileOutputStream( target );
353
354                final byte[] buffer = new byte[ 65536 ];
355
356                for ( int read = in.read( buffer );
357                      read >= 0;
358                      out.write( buffer, 0, read ), read = in.read( buffer ) );
359
360                out.close();
361                out = null;
362
363                in.close();
364                in = null;
365            }
366        }
367        finally
368        {
369            try
370            {
371                if ( out != null )
372                {
373                    out.close();
374                }
375            }
376            catch ( final IOException e )
377            {
378                this.getLog().warn( e );
379            }
380            finally
381            {
382                try
383                {
384                    if ( in != null )
385                    {
386                        in.close();
387                    }
388                }
389                catch ( final IOException e )
390                {
391                    this.getLog().warn( e );
392                }
393            }
394        }
395    }
396
397}