View Javadoc

1   /*
2    * Copyright 2012 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.ci.maven.mojo;
17  
18  import java.io.File;
19  import java.io.FileNotFoundException;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.net.MalformedURLException;
23  import java.net.URL;
24  
25  import org.apache.commons.io.FileUtils;
26  import org.apache.maven.execution.MavenSession;
27  import org.apache.maven.model.CiManagement;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.project.MavenProject;
30  import org.apache.maven.settings.Server;
31  import org.apache.maven.settings.Settings;
32  import org.codehaus.plexus.util.StringUtils;
33  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
34  import org.xml.sax.InputSource;
35  
36  import de.smartics.ci.comm.LogHelper;
37  import de.smartics.ci.config.load.HudsonJobConfig;
38  import de.smartics.ci.config.load.LocationManager;
39  import de.smartics.ci.config.maven.MavenConfig;
40  import de.smartics.ci.config.maven.MavenPom;
41  import de.smartics.ci.config.maven.MavenSettings;
42  import de.smartics.ci.maven.CiServer;
43  import de.smartics.ci.maven.Logger;
44  import de.smartics.ci.maven.util.SettingsDecrypter;
45  
46  /**
47   * Base implementation for all CI build mojos.
48   */
49  public abstract class AbstractCiMojo extends AbstractLoggingMojo
50  {
51    // ********************************* Fields *********************************
52  
53    // --- constants ------------------------------------------------------------
54  
55    // --- members --------------------------------------------------------------
56  
57    // ... Mojo infrastructure ..................................................
58  
59    /**
60     * The Maven project.
61     *
62     * @parameter expression="${project}"
63     * @required
64     * @readonly
65     * @since 1.0
66     */
67    protected MavenProject project;
68  
69    /**
70     * The user's settings.
71     *
72     * @parameter expression="${settings}"
73     * @required
74     * @readonly
75     * @since 1.0
76     */
77    protected Settings settings;
78  
79    /**
80     * Helper to decrypt passwords from the settings.
81     *
82     * @component
83     *            role="org.sonatype.plexus.components.sec.dispatcher.SecDispatcher"
84     * @since 1.0
85     */
86    private SecDispatcher securityDispatcher;
87  
88    /**
89     * The location of the <code>settings-security.xml</code>.
90     *
91     * @parameter expression="${user.home}/.m2/settings-security.xml"
92     * @required
93     * @since 1.0
94     */
95    private String settingsSecurityLocation;
96  
97    /**
98     * The Maven session instance.
99     *
100    * @parameter expression="${session}"
101    * @required
102    * @readonly
103    */
104   protected MavenSession session;
105 
106   /**
107    * The path to the project local configuration files for CI. If
108    * <code>${basedir}/src/etc/ci</code> exists, it is automatically added.
109    *
110    * @parameter
111    * @since 1.0
112    */
113   protected String[] hudsonConfigSourceLocations;
114 
115   /**
116    * The name of the server configuration in the <code>settings.xml</code> that
117    * provides credentials to access the Hudson server. Only used if no server
118    * matches the POM's <code>ciManagement/url</code> and
119    * <code>ciManagement/system</code>.
120    *
121    * @parameter default-value="hudson"
122    * @since 1.0
123    */
124   protected String ciServerId;
125 
126   /**
127    * The path to the configuration file for CI. Defaults to
128    * <code>${basedir}/src/etc/ci/ci-config.xml</code>.
129    *
130    * @parameter default-value= "${basedir}/src/etc/ci/ci-config.xml"
131    * @since 1.0
132    */
133   protected File ciConfigurationFile;
134 
135   /**
136    * The path to the destination folder to write the Hudson configurations to.
137    * Defaults to <code>${project.build.directory}/hudson</code>.
138    *
139    * @parameter default-value= "${project.build.directory}/hudson"
140    * @since 1.0
141    */
142   protected File hudsonTargetLocation;
143 
144   // ****************************** Initializer *******************************
145 
146   // ****************************** Constructors ******************************
147 
148   // ****************************** Inner Classes *****************************
149 
150   // ********************************* Methods ********************************
151 
152   // --- init -----------------------------------------------------------------
153 
154   // --- get&set --------------------------------------------------------------
155 
156   /**
157    * Returns the Maven project.
158    *
159    * @return the Maven project.
160    */
161   public final MavenProject getProject()
162   {
163     return project;
164   }
165 
166   /**
167    * Sets the Maven project.
168    *
169    * @param project the Maven project.
170    */
171   public final void setProject(final MavenProject project)
172   {
173     this.project = project;
174   }
175 
176   /**
177    * Sets the Maven session instance.
178    *
179    * @param session the Maven session instance.
180    */
181   public final void setSession(final MavenSession session)
182   {
183     this.session = session;
184   }
185 
186   // --- business -------------------------------------------------------------
187 
188   /**
189    * Creates a configured location manager to fetch Hudson configuration files.
190    *
191    * @return a configured location manager to fetch Hudson configuration files.
192    */
193   protected final LocationManager createLocationManager()
194   {
195     final ClassLoader classLoader =
196         Thread.currentThread().getContextClassLoader();
197     final LocationManager locationManager = new LocationManager(classLoader);
198 
199     provideDefaultLocation(locationManager);
200 
201     if (hudsonConfigSourceLocations != null)
202     {
203       for (final String location : hudsonConfigSourceLocations)
204       {
205         final File dir = new File(location);
206         if (dir.exists())
207         {
208           locationManager.addLocation(dir);
209         }
210         else
211         {
212           final File relativeDir = new File(project.getBasedir(), location);
213           if (relativeDir.exists())
214           {
215             locationManager.addLocation(relativeDir);
216           }
217           else
218           {
219             try
220             {
221               final URL url = new URL(location);
222               locationManager.addLocation(url);
223             }
224             catch (final MalformedURLException e)
225             {
226               final Logger logger = getLog();
227               LogHelper.logWarn(logger, logLevel, "Cannot add location '"
228                                                   + location + "'. Skipping.");
229             }
230           }
231         }
232       }
233     }
234 
235     return locationManager;
236   }
237 
238   private void provideDefaultLocation(final LocationManager locationManager)
239   {
240     final File defaultDir = new File(project.getBasedir(), "/src/etc/ci");
241     if (defaultDir.exists())
242     {
243       locationManager.addLocation(defaultDir);
244     }
245   }
246 
247   /**
248    * Creates a Maven configuration that contains the relevant information for
249    * the Hudson configuration files.
250    *
251    * @return a Maven configuration that contains the relevant information for
252    *         the Hudson configuration files.
253    */
254   protected final MavenConfig createMavenConfig()
255   {
256     final MavenPom pom = new MavenPom();
257     pom.setGroupId(project.getGroupId());
258     pom.setArtifactId(project.getArtifactId());
259     pom.setDescription(project.getDescription());
260     pom.setScmConnectionString(project.getScm().getDeveloperConnection());
261 
262     final MavenSettings settings = new MavenSettings();
263 
264     final MavenConfig mavenConfig = new MavenConfig(settings, pom);
265     return mavenConfig;
266   }
267 
268   /**
269    * Opens a stream to the {@link #ciConfigurationFile}.
270    * <p>
271    * The client is responsible to close the stream.
272    * </p>
273    *
274    * @return the stream inside an input source.
275    * @throws MojoExecutionException on any problem opening the stream.
276    */
277   protected final InputSource createInputSource() throws MojoExecutionException
278   {
279     final File ciConfigXml = ciConfigurationFile;
280     try
281     {
282       final InputSource is;
283       if (checkCiConfigXml(ciConfigXml))
284       {
285         is = fetchLocal(ciConfigXml);
286       }
287       else
288       {
289         is = fetchClassloader();
290       }
291       return is;
292     }
293     catch (final IOException e)
294     {
295       throw new MojoExecutionException("Cannot open file 'ci-config.xml'.", e);
296     }
297 
298   }
299 
300   private boolean checkCiConfigXml(final File ciConfigXml)
301   {
302     return ciConfigXml != null && ciConfigXml.exists();
303   }
304 
305   private InputSource fetchClassloader() throws FileNotFoundException
306   {
307     final InputSource is;
308     final ClassLoader classLoader =
309         Thread.currentThread().getContextClassLoader();
310     final LocationManager lm = new LocationManager(classLoader);
311     is = lm.open("ci-config.xml");
312     return is;
313   }
314 
315   private InputSource fetchLocal(final File ciConfigXml) throws IOException
316   {
317     final InputSource is;
318     is = new InputSource(ciConfigXml.getAbsolutePath());
319     final InputStream in = FileUtils.openInputStream(ciConfigXml);
320     is.setByteStream(in);
321     return is;
322   }
323 
324   /**
325    * Writes the Hudson job configuration XML file to its destination folder.
326    *
327    * @param hudsonJobConfig the Hudson job XML document.
328    * @throws MojoExecutionException on any problem writing the file.
329    */
330   protected final void writeHudsonConfigXml(
331       final HudsonJobConfig hudsonJobConfig) throws MojoExecutionException
332   {
333     ensureDirectoryExists(hudsonTargetLocation);
334 
335     final File file =
336         new File(hudsonTargetLocation, hudsonJobConfig.getId() + ".xml");
337     try
338     {
339       FileUtils.writeStringToFile(file, hudsonJobConfig.getConfigXml());
340     }
341     catch (final IOException e)
342     {
343       throw new MojoExecutionException(
344           "Cannot write Hudson configuration file to '" + file.getPath() + "'",
345           e);
346     }
347   }
348 
349   private static void ensureDirectoryExists(final File dir)
350     throws MojoExecutionException
351   {
352     if (!dir.exists())
353     {
354       if (!dir.mkdirs()) // NOPMD
355       {
356         throw new MojoExecutionException("Cannot generate directory '"
357                                          + dir.getPath() + "'");
358       }
359     }
360   }
361 
362   /**
363    * Returns the Hudson server configuration to run a remote access.
364    *
365    * @return the server information. If no access information is found, only the
366    *         URL to the CI server is returned.
367    * @throws MojoExecutionException if no CI URL is specified.
368    */
369   protected final CiServer getCiServer() throws MojoExecutionException
370   {
371     final CiManagement ciManagement = project.getCiManagement();
372     final Server server = getCiServerFromSettings(ciManagement);
373     final String ciUrl = ciManagement.getUrl();
374 
375     final SettingsDecrypter settingsDecrypter =
376         new SettingsDecrypter(securityDispatcher, settingsSecurityLocation);
377 
378     return new CiServer(logger, server, ciUrl, settingsDecrypter);
379   }
380 
381   private Server getCiServerFromSettings(final CiManagement ciManagement)
382     throws MojoExecutionException
383   {
384     Server server;
385 
386     if (StringUtils.isNotBlank(ciServerId))
387     {
388       server = settings.getServer(ciServerId);
389     }
390     else
391     {
392       final String ciUrl = ciManagement.getUrl();
393       server = settings.getServer(ciUrl);
394       if (server != null)
395       {
396         return server;
397       }
398 
399       final String ciName = ciManagement.getSystem();
400       if (StringUtils.isNotBlank(ciName))
401       {
402         server = settings.getServer(ciName);
403       }
404     }
405 
406     return server;
407   }
408 
409   // CHECKSTYLE:OFF
410   /**
411    * Provides a check for conditions to be met to run this plugin successfully.
412    * <p>
413    * Subclasses may add additional checks by overriding this method and calling
414    * this it in their implementation.
415    * </p>
416    *
417    * @throws MojoExecutionException if the preconditions are not met.
418    */
419   protected void checkPreconditions() throws MojoExecutionException
420   {
421     // CHECKSTYLE:ON
422     if (project == null)
423     {
424       throw new MojoExecutionException(
425           "No project information provided. Please run this plugin on"
426               + " a project that provides a POM.");
427     }
428 
429     final CiManagement ciManagement = project.getCiManagement();
430     if (ciManagement == null)
431     {
432       throw new MojoExecutionException(
433           "No CI management specified. Please provide CI information"
434               + " within the 'ciManagement' block of your POM.");
435     }
436 
437     final String ciUrl = ciManagement.getUrl();
438     if (StringUtils.isBlank(ciUrl))
439     {
440       throw new MojoExecutionException(
441           "No CI URL specified. Please provide CI information within the"
442               + " 'ciManagement/url' block of your POM.");
443     }
444   }
445 
446 }