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}