001package org.andromda.maven.plugin.configuration; 002 003import java.io.File; 004import java.io.IOException; 005import java.io.StringReader; 006import java.net.MalformedURLException; 007import java.net.URL; 008import java.net.URLClassLoader; 009import java.util.List; 010import java.util.Properties; 011import org.andromda.core.common.ResourceUtils; 012import org.andromda.core.configuration.Configuration; 013import org.apache.commons.lang.ObjectUtils; 014import org.apache.maven.artifact.Artifact; 015import org.apache.maven.artifact.factory.ArtifactFactory; 016import org.apache.maven.artifact.repository.ArtifactRepository; 017import org.apache.maven.model.Dependency; 018import org.apache.maven.model.Plugin; 019import org.apache.maven.plugin.AbstractMojo; 020import org.apache.maven.project.MavenProject; 021import org.apache.maven.settings.Settings; 022import org.apache.maven.shared.filtering.PropertyUtils; 023import org.codehaus.plexus.util.InterpolationFilterReader; 024 025/** 026 * An abstract Mojo for dealing with the AndroMDA configuration, 027 * if a plugin needs to use the AndroMDA configuration, it should extend this 028 * class. 029 * 030 * @author Chad Brandon 031 * @author Bob Fields 032 */ 033public abstract class AbstractConfigurationMojo 034 extends AbstractMojo 035{ 036 /** 037 * The path to the mappings within the plugin. 038 */ 039 private static final String MAPPINGS_PATH = "META-INF/andromda/mappings"; 040 041 /** 042 * Creates the Configuration instance from the {@link URL} 043 * 044 * @param configurationUri 045 * @return the configuration instance 046 * @throws IOException if the URL is invalid. 047 */ 048 protected Configuration getConfiguration(final URL configurationUri) 049 throws IOException 050 { 051 final String contents = this.replaceProperties(ResourceUtils.getContents(configurationUri)); 052 final Configuration configuration = Configuration.getInstance(contents); 053 final URL mappingsUrl = ResourceUtils.getResource(MAPPINGS_PATH); 054 if (mappingsUrl != null) 055 { 056 configuration.addMappingsSearchLocation(mappingsUrl.toString()); 057 } 058 return configuration; 059 } 060 061 /** 062 * Collects and returns all properties as a Properties instance. 063 * 064 * @return the properties including those from the project, settings, etc. 065 * @throws IOException 066 */ 067 protected Properties getProperties() 068 throws IOException 069 { 070 // System properties 071 final Properties properties = new Properties(); 072 073 properties.put( 074 "settings", 075 this.getSettings()); 076 077 // - project properties 078 properties.putAll(this.getProject().getProperties()); 079 for (final String propertiesFile : (Iterable<String>) this.getPropertyFiles()) 080 { 081 final Properties projectProperties = PropertyUtils.loadPropertyFile( 082 new File(propertiesFile), 083 true, 084 true); 085 086 properties.putAll(projectProperties); 087 } 088 089 for (Object objProperty : properties.keySet()) 090 { 091 final String property = (String) objProperty; 092 final String value = this.replaceProperties( 093 properties, 094 ObjectUtils.toString(properties.get(property))); 095 properties.put( 096 property, 097 value); 098 } 099 100 properties.putAll(System.getProperties()); 101 102 return properties; 103 } 104 105 /** 106 * Replaces all properties having the style 107 * <code>${some.property}</code> with the value 108 * of the specified property if there is one. 109 * 110 * @param string the string to perform replacement on. 111 * @return this.replaceProperties(this.getProperties(),string); 112 * @throws IOException 113 */ 114 protected String replaceProperties(final String string) 115 throws IOException 116 { 117 return this.replaceProperties( 118 this.getProperties(), 119 string); 120 } 121 122 /** 123 * The begin token for interpolation. 124 */ 125 private static final String BEGIN_TOKEN = "${"; 126 127 /** 128 * The end token for interpolation. 129 */ 130 private static final String END_TOKEN = "}"; 131 132 /** 133 * Replaces all properties having the style 134 * <code>${some.property}</code> with the value 135 * of the specified property if there is one. 136 * 137 * @param properties the properties used to perform the replacement. 138 * @param string the fileContents to perform replacement on. 139 */ 140 private String replaceProperties( 141 final Properties properties, 142 final String string) 143 throws IOException 144 { 145 final StringReader stringReader = new StringReader(string); 146 InterpolationFilterReader reader = new InterpolationFilterReader(stringReader, properties, "${", "}"); 147 reader.reset(); 148 reader = new InterpolationFilterReader( 149 reader, 150 new BeanProperties(this.getProject()), 151 BEGIN_TOKEN, 152 END_TOKEN); 153 reader = new InterpolationFilterReader( 154 reader, 155 new BeanProperties(this.getSettings()), 156 BEGIN_TOKEN, 157 END_TOKEN); 158 return ResourceUtils.getContents(reader); 159 } 160 161 /** 162 * Sets the current context class loader from the given runtime classpath elements. 163 * 164 * @param classpathFiles 165 * @throws MalformedURLException 166 */ 167 protected void initializeClasspathFromClassPathElements(final List<String> classpathFiles) 168 throws MalformedURLException 169 { 170 if (classpathFiles != null && !classpathFiles.isEmpty()) 171 { 172 final URL[] classpathUrls = new URL[classpathFiles.size()]; 173 174 for (int ctr = 0; ctr < classpathFiles.size(); ++ctr) 175 { 176 final File file = new File(classpathFiles.get(ctr)); 177 if (this.getLog().isDebugEnabled()) 178 { 179 getLog().debug("adding to classpath '" + file + '\''); 180 } 181 classpathUrls[ctr] = file.toURI().toURL(); 182 } 183 184 final URLClassLoader loader = 185 new ConfigurationClassLoader(classpathUrls, 186 Thread.currentThread().getContextClassLoader()); 187 Thread.currentThread().setContextClassLoader(loader); 188 } 189 } 190 191 /** 192 * Adds any dependencies to the current project from the plugin 193 * having the given <code>pluginArtifactId</code>. 194 * 195 * @param pluginArtifactId the artifactId of the plugin of which to add its dependencies. 196 * @param scope the artifact scope in which to add them (runtime, compile, etc). 197 */ 198 protected void addPluginDependencies( 199 final String pluginArtifactId, 200 final String scope) 201 { 202 if (pluginArtifactId != null) 203 { 204 final List<Plugin> plugins = this.getPlugins(); 205 if (plugins != null) 206 { 207 for (final Plugin plugin : plugins) 208 { 209 if (pluginArtifactId.equals(plugin.getArtifactId())) 210 { 211 final List<Dependency> dependencies = plugin.getDependencies(); 212 if (dependencies != null) 213 { 214 for (Dependency dependency : plugin.getDependencies()) 215 { 216 this.addDependency( 217 dependency, 218 scope); 219 } 220 } 221 } 222 } 223 } 224 } 225 } 226 227 // Can't do anything about raw Collection types in MavenProject dependency 228 @SuppressWarnings("unchecked") 229 /** 230 * Adds a dependency to the current project's dependencies. 231 * 232 * @param dependency 233 */ 234 private void addDependency( 235 final Dependency dependency, 236 final String scope) 237 { 238 final ArtifactRepository localRepository = this.getLocalRepository(); 239 final MavenProject project = this.getProject(); 240 if (project != null && localRepository != null) 241 { 242 if (dependency != null) 243 { 244 final Artifact artifact = 245 this.getFactory().createArtifact( 246 dependency.getGroupId(), 247 dependency.getArtifactId(), 248 dependency.getVersion(), 249 scope, 250 dependency.getType()); 251 final File file = new File( 252 localRepository.getBasedir(), 253 localRepository.pathOf(artifact)); 254 artifact.setFile(file); 255 project.getDependencies().add(dependency); 256 project.getArtifacts().add(artifact); 257 } 258 } 259 } 260 261 /** 262 * Gets the current project. 263 * 264 * @return Returns the project. 265 */ 266 protected abstract MavenProject getProject(); 267 268 /** 269 * Gets the property files for the profile (the ones defined 270 * in the filters section of the POM). 271 * 272 * @return Returns the propertyFiles. 273 */ 274 protected abstract List<String> getPropertyFiles(); 275 276 /** 277 * Gets the current settings of the project. 278 * 279 * @return Returns the settings. 280 */ 281 protected abstract Settings getSettings(); 282 283 /** 284 * Gets the artifact factory used to construct any new required artifacts. 285 * @return ArtifactFactory 286 */ 287 protected abstract ArtifactFactory getFactory(); 288 289 /** 290 * Gets the current project's registered plugin implementations. 291 * @return pluginList 292 * 293 * @parameter expression="${project.build.plugins}" 294 * @required 295 * @readonly 296 */ 297 protected abstract List<Plugin> getPlugins(); 298 299 /** 300 * Gets the current local repository instance. 301 * 302 * @return the local repository instance of the current project. 303 */ 304 protected abstract ArtifactRepository getLocalRepository(); 305 306 /** 307 * Set this to 'true' to bypass cartridge tests entirely. Its use is NOT RECOMMENDED, but quite convenient on occasion. 308 * 309 * @parameter expression="${maven.test.skip}" default-value="false" 310 */ 311 protected boolean skip; 312 313 /** 314 * Set this to 'true' to skip running tests, but still compile them. Its use is NOT RECOMMENDED, but quite convenient on occasion. 315 * 316 * @parameter expression="${skipTests}" default-value="false" 317 */ 318 protected boolean skipTests; 319 320 /** 321 * Set this to true to ignore a failure during testing. Its use is NOT RECOMMENDED, but quite convenient on occasion. 322 * 323 * @parameter expression="${maven.test.failure.ignore}" default-value="false" 324 */ 325 protected boolean testFailureIgnore; 326}