001package org.andromda.maven.plugin.bootstrap.install;
002
003import org.apache.commons.collections.CollectionUtils;
004import org.apache.commons.collections.Predicate;
005import org.apache.commons.lang.StringUtils;
006import org.apache.maven.artifact.Artifact;
007import org.apache.maven.artifact.repository.ArtifactRepository;
008import org.apache.maven.model.Dependency;
009import org.apache.maven.model.Model;
010import org.apache.maven.plugin.AbstractMojo;
011import org.apache.maven.plugin.MojoExecutionException;
012import org.apache.maven.plugin.MojoFailureException;
013import org.apache.maven.project.MavenProject;
014import org.apache.commons.io.FileUtils;
015
016import java.io.File;
017import java.io.FileWriter;
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.List;
021
022
023/**
024 * Provides the installation of bootstrap artifacts.
025 *
026 * @author Chad Brandon
027 * @goal install
028 * @phase install
029 */
030public class BootstrapInstallMojo
031        extends AbstractMojo
032{
033    /**
034     * The maven project.
035     *
036     * @parameter expression="${project}"
037     * @required
038     * @readonly
039     * @description "the maven project to use"
040     */
041    protected MavenProject project;
042
043    /**
044     * @parameter expression="${localRepository}"
045     * @required
046     * @readonly
047     */
048    protected ArtifactRepository localRepository;
049
050    /**
051     * @parameter expression="${bootstrap.artifacts}"
052     * @description whether or not bootstrap artifacts should be installed, by default they are.
053     */
054    protected boolean installBootstraps = true;
055
056    /**
057     * @parameter
058     * @required
059     * @description the directory to which the bootstrap artifact should be installed.
060     */
061    protected String installDirectory;
062
063    /**
064     * @parameter expression="org.andromda"
065     * @required
066     * @readonly
067     * @description the name of the project groupId.
068     */
069    protected String projectGroupId;
070
071    /**
072     * @parameter expression="org.andromda.bootstrap"
073     * @required
074     * @readonly
075     * @description the name of the project bootstrap groupId.
076     */
077    protected String projectBootstrapGroupId;
078
079    /**
080     * The extension for "JAR" files.
081     */
082    private static final String JAR_EXTENSION = "jar";
083
084    /**
085     * The extension for "POM" files.
086     */
087    private static final String POM_EXTENSION = "pom";
088
089    /**
090     * The qualifier for source artifacts.
091     */
092    private static final String SOURCES_QUALIFIER = "-sources";
093
094    /**
095     * The qualifier for javadoc artifacts.
096     */
097    private static final String JAVADOC_QUALIFIER = "-javadoc";
098
099    /**
100     * The qualifier for test artifacts.
101     */
102    private static final String TEST_QUALIFIER = "-test";
103
104    /**
105     * @see org.apache.maven.plugin.Mojo#execute()
106     */
107    public void execute()
108            throws MojoExecutionException, MojoFailureException
109    {
110        if (this.installBootstraps)
111        {
112            try
113            {
114                final File installDirectory = new File(this.installDirectory);
115                if (!installDirectory.exists() || !installDirectory.isDirectory())
116                {
117                    throw new MojoExecutionException("'" + installDirectory + "' is not a valid install directory");
118                }
119                Artifact artifact = this.project.getArtifact();
120
121                final String path = this.replaceExtension(artifact, JAR_EXTENSION, null);
122                final String localRepositoryDirectory = this.localRepository.getBasedir();
123                final File existingFile = new File(localRepositoryDirectory, path);
124                final String bootstrapGroupId = this.getBootstrapGroupId(artifact);
125                final String bootstrapPath = bootstrapGroupId.replace('.', '/')
126                    + '/' + artifact.getArtifactId();
127                final File bootstrapPomFile = new File(installDirectory, bootstrapPath + '.' + POM_EXTENSION);
128                this.writeMinimalPom(bootstrapPomFile);
129                final File bootstrapFile = new File(installDirectory, bootstrapPath + '.' + JAR_EXTENSION);
130                this.getLog().info("Installing bootstrap artifact: " + bootstrapFile);
131                if (path != null)
132                {
133                    FileUtils.copyFile(existingFile, bootstrapFile);
134                }
135                final String sourcePath = this.replaceExtension(artifact, JAR_EXTENSION, SOURCES_QUALIFIER);
136                if (sourcePath != null)
137                {
138                    this.getLog().info("Installing bootstrap artifact: " + sourcePath);
139                    FileUtils.copyFile(new File(localRepositoryDirectory, sourcePath), new File(installDirectory, bootstrapPath + SOURCES_QUALIFIER + '.' + JAR_EXTENSION));
140                }
141                final String javadocPath = this.replaceExtension(artifact, JAR_EXTENSION, JAVADOC_QUALIFIER);
142                if (javadocPath != null)
143                {
144                    this.getLog().info("Installing bootstrap artifact: " + javadocPath);
145                    FileUtils.copyFile(new File(localRepositoryDirectory, javadocPath), new File(installDirectory, bootstrapPath + JAVADOC_QUALIFIER + '.' + JAR_EXTENSION));
146                }
147                final String testSourcePath = this.replaceExtension(artifact, JAR_EXTENSION, TEST_QUALIFIER + SOURCES_QUALIFIER);
148                if (testSourcePath != null)
149                {
150                    this.getLog().info("Installing bootstrap artifact: " + testSourcePath);
151                    FileUtils.copyFile(new File(localRepositoryDirectory, testSourcePath), new File(installDirectory, bootstrapPath + TEST_QUALIFIER + SOURCES_QUALIFIER + '.' + JAR_EXTENSION));
152                }
153                final String testJavadocPath = this.replaceExtension(artifact, JAR_EXTENSION, TEST_QUALIFIER + JAVADOC_QUALIFIER);
154                if (testJavadocPath != null)
155                {
156                    this.getLog().info("Installing bootstrap artifact: " + testJavadocPath);
157                    FileUtils.copyFile(new File(localRepositoryDirectory, testJavadocPath), new File(installDirectory, bootstrapPath + TEST_QUALIFIER + JAVADOC_QUALIFIER + '.' + JAR_EXTENSION));
158                }
159            }
160            catch (final Throwable throwable)
161            {
162                throw new MojoExecutionException("Error creating bootstrap artifact", throwable);
163            }
164        }
165    }
166
167    /**
168     * Clears the POM's model of its parent or any dependencies
169     * it may have so that we can write a POM that isn't dependent on anything
170     * (which we need for bootstrap artifacts).
171     *
172     * @param bootstrapPomFile the bootstrap POM file to write.
173     */
174    private void writeMinimalPom(final File bootstrapPomFile)
175            throws IOException
176    {
177        final Model model = this.project.getModel();
178
179        final MavenProject minimalProject = new MavenProject();
180        final Model minModel = minimalProject.getModel();
181
182        minModel.setGroupId(getBootstrapGroupId(this.project.getArtifact()));
183        minModel.setArtifactId(model.getArtifactId());
184        minModel.setVersion(model.getVersion());
185        minModel.setName(model.getName());
186        minModel.setPackaging("jar");
187        minModel.setDescription(model.getDescription());
188        minModel.setModelEncoding(model.getModelEncoding());
189        minModel.setModelVersion(model.getModelVersion());
190        minModel.setUrl(model.getUrl());
191        minModel.setScm(model.getScm());
192        minModel.setDevelopers(model.getDevelopers());
193        minModel.setCiManagement(model.getCiManagement());
194        minModel.setIssueManagement(model.getIssueManagement());
195
196        minModel.setPrerequisites(model.getPrerequisites());
197        minModel.setOrganization(model.getOrganization());
198        minModel.setInceptionYear(model.getInceptionYear());
199        minModel.setLicenses(model.getLicenses());
200
201        final List<Dependency> dependencies = new ArrayList<Dependency>(model.getDependencies());
202        // filter all of andromda dependencies away
203        CollectionUtils.filter(dependencies, new Predicate()
204        {
205            public boolean evaluate(Object object)
206            {
207                Dependency dependency = (Dependency)object;
208                final String lGroupId = dependency.getGroupId();
209                return !lGroupId.startsWith("org.andromda") || lGroupId.startsWith("org.andromda.thirdparty");
210            }
211        });
212        minModel.setDependencies(dependencies);
213
214        final FileWriter fileWriter = new FileWriter(bootstrapPomFile);
215        minimalProject.writeModel(fileWriter);
216        fileWriter.flush();
217    }
218
219    /**
220     * Retrieves the project's bootstrap groupId from the given <code>artifact</code>.
221     *
222     * @param artifact the artifact from which to retrieve the group Id.
223     * @return the bootstrap groupId.
224     */
225    private String getBootstrapGroupId(final Artifact artifact)
226    {
227        return StringUtils.replaceOnce(
228                artifact.getGroupId(),
229                this.projectGroupId,
230                this.projectBootstrapGroupId);
231    }
232
233    /**
234     * Retrieves the repository artifact file name with extension removed and qualifier added.
235     * If the file does not yet exist in the local repository, returns null
236     *
237     * @param artifact     the artifact from which to retrieve the version information.
238     * @param newExtension new extension for the file
239     * @param qualifier the -qualifier for the repo artifact such as -sources -javadocs -test-sources -test-javadocs
240     * @return the artifact name with no version and extension removed.
241     */
242    private String replaceExtension(
243            final Artifact artifact,
244            final String newExtension,
245            final String qualifier)
246    {
247        String path = this.localRepository.pathOf(artifact);
248        File artifactFile = new File(this.localRepository.getBasedir(), path);
249        if (!artifactFile.exists())
250        {
251            this.getLog().error("Bootstrap artifact does not exist: " + path);
252            return null;
253        }
254        final String version = artifact.getVersion() != null ? artifact.getVersion().trim() : "";
255        int versionIndex = path.lastIndexOf(artifact.getVersion());
256        final String extension = path.substring(
257                versionIndex + version.length() + 1,
258                path.length());
259        int extensionIndex = path.lastIndexOf(extension);
260        if (StringUtils.isNotBlank(qualifier))
261        {
262            path = path.substring(0, extensionIndex-1) + qualifier + '.' + extension;
263            File qualifiedFile = new File(this.localRepository.getBasedir(), path);
264            if (!qualifiedFile.exists())
265            {
266                this.getLog().warn("Bootstrap qualified artifact does not exist: " + path);
267                return null;
268            }
269        }
270        if (!newExtension.equals(extension))
271        {
272            path = path.substring(0, extensionIndex) + newExtension;
273        }
274        return path;
275    }
276}