View Javadoc

1   /*
2    * Copyright 2006-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.maven.plugin.buildmetadata.scm.maven;
17  
18  import java.io.File;
19  import java.io.Serializable;
20  import java.util.List;
21  
22  import org.apache.maven.scm.ScmBranch;
23  import org.apache.maven.scm.ScmFileSet;
24  import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
25  import org.apache.maven.scm.command.changelog.ChangeLogSet;
26  import org.apache.maven.scm.provider.ScmProvider;
27  import org.apache.maven.scm.repository.ScmRepository;
28  import org.codehaus.plexus.util.StringUtils;
29  
30  import de.smartics.maven.plugin.buildmetadata.scm.ScmException;
31  
32  /**
33   * Provides access information to retrieve revision information from the SCM.
34   *
35   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
36   * @version $Revision:591 $
37   */
38  public final class ScmAccessInfo implements Serializable
39  {
40    // ********************************* Fields *********************************
41  
42    // --- constants ------------------------------------------------------------
43  
44    /**
45     * The class version identifier.
46     * <p>
47     * The value of this constant is {@value}.
48     * </p>
49     */
50    private static final long serialVersionUID = 1L;
51  
52    /**
53     * The number of retries to fetch the change log if the first attempt failed
54     * to return a non empty set.
55     * <p>
56     * The value of this constant is {@value}.
57     */
58    public static final int DEFAULT_RETRY_COUNT = 5;
59  
60    // --- members --------------------------------------------------------------
61  
62    /**
63     * The format of the dates understood by the SCM system.
64     */
65    private String dateFormat;
66  
67    /**
68     * The root directory that contains the files under SCM control.
69     */
70    private File rootDirectory;
71  
72    /**
73     * The range of the query in days to fetch change log entries from the SCM. If
74     * no change logs have been found, the range is incremented up to
75     * {@value #DEFAULT_RETRY_COUNT} times. If no change log has been found after
76     * these {@value #DEFAULT_RETRY_COUNT} additional queries, the revision number
77     * will not be set with a valid value.
78     */
79    private int queryRangeInDays;
80  
81    /**
82     * The flag to fail if local modifications have been found. The value is
83     * <code>true</code> if the build should fail if there are modifications (any
84     * files not in-sync with the remote repository), <code>false</code> if the
85     * fact is only to be noted in the build properties.
86     */
87    private boolean failOnLocalModifications;
88  
89    /**
90     * The flag to ignore files and directories starting with a dot for checking
91     * modified files. This implicates that any files or directories, starting
92     * with a dot, are ignored when the check on changed files is run. If the
93     * value is <code>true</code>, dot files are ignored, if it is set to
94     * <code>false</code>, dot files are respected.
95     */
96    private boolean ignoreDotFilesInBaseDir;
97  
98    // ****************************** Initializer *******************************
99  
100   // ****************************** Constructors ******************************
101 
102   /**
103    * Default constructor.
104    */
105   public ScmAccessInfo()
106   {
107   }
108 
109   // ****************************** Inner Classes *****************************
110 
111   // ********************************* Methods ********************************
112 
113   // --- init -----------------------------------------------------------------
114 
115   // --- get&set --------------------------------------------------------------
116 
117   /**
118    * Returns the format of the dates understood by the SCM system.
119    *
120    * @return the format of the dates understood by the SCM system.
121    */
122   public String getDateFormat()
123   {
124     return dateFormat;
125   }
126 
127   /**
128    * Sets the format of the dates understood by the SCM system.
129    *
130    * @param dateFormat the format of the dates understood by the SCM system.
131    */
132   public void setDateFormat(final String dateFormat)
133   {
134     this.dateFormat = dateFormat;
135   }
136 
137   /**
138    * Returns the root directory that contains the files under SCM control.
139    *
140    * @return the root directory that contains the files under SCM control.
141    */
142   public File getRootDirectory()
143   {
144     return rootDirectory;
145   }
146 
147   /**
148    * Sets the root directory that contains the files under SCM control.
149    *
150    * @param rootDirectory the root directory that contains the files under SCM
151    *          control.
152    */
153   public void setRootDirectory(final File rootDirectory)
154   {
155     this.rootDirectory = rootDirectory;
156   }
157 
158   /**
159    * Returns the range of the query in days to fetch change log entries from the
160    * SCM. If no change logs have been found, the range is incremented up to
161    * {@value #DEFAULT_RETRY_COUNT} times. If no change log has been found after
162    * these {@value #DEFAULT_RETRY_COUNT} additional queries, the revision number
163    * will not be set with a valid value.
164    *
165    * @return the range of the query in days to fetch change log entries from the
166    *         SCM.
167    */
168   public int getQueryRangeInDays()
169   {
170     return queryRangeInDays;
171   }
172 
173   /**
174    * Sets the range of the query in days to fetch change log entries from the
175    * SCM. If no change logs have been found, the range is incremented up to
176    * {@value #DEFAULT_RETRY_COUNT} times. If no change log has been found after
177    * these {@value #DEFAULT_RETRY_COUNT} additional queries, the revision number
178    * will not be set with a valid value.
179    *
180    * @param queryRangeInDays the range of the query in days to fetch change log
181    *          entries from the SCM.
182    */
183   public void setQueryRangeInDays(final int queryRangeInDays)
184   {
185     this.queryRangeInDays = queryRangeInDays;
186   }
187 
188   /**
189    * Returns the flag to fail if local modifications have been found. The value
190    * is <code>true</code> if the build should fail if there are modifications
191    * (any files not in-sync with the remote repository), <code>false</code> if
192    * the fact is only to be noted in the build properties.
193    *
194    * @return the flag to fail if local modifications have been found.
195    */
196   public boolean isFailOnLocalModifications()
197   {
198     return failOnLocalModifications;
199   }
200 
201   /**
202    * Sets the flag to fail if local modifications have been found. The value is
203    * <code>true</code> if the build should fail if there are modifications (any
204    * files not in-sync with the remote repository), <code>false</code> if the
205    * fact is only to be noted in the build properties.
206    *
207    * @param failOnLocalModifications the flag to fail if local modifications
208    *          have been found.
209    */
210   public void setFailOnLocalModifications(final boolean failOnLocalModifications)
211   {
212     this.failOnLocalModifications = failOnLocalModifications;
213   }
214 
215   /**
216    * Returns the flag to ignore files and directories starting with a dot for
217    * checking modified files. This implicates that any files or directories,
218    * starting with a dot, are ignored when the check on changed files is run. If
219    * the value is <code>true</code>, dot files are ignored, if it is set to
220    * <code>false</code>, dot files are respected.
221    *
222    * @return the flag to ignore files and directories starting with a dot for
223    *         checking modified files.
224    */
225   public boolean isIgnoreDotFilesInBaseDir()
226   {
227     return ignoreDotFilesInBaseDir;
228   }
229 
230   /**
231    * Sets the flag to ignore files and directories starting with a dot for
232    * checking modified files. This implicates that any files or directories,
233    * starting with a dot, are ignored when the check on changed files is run. If
234    * the value is <code>true</code>, dot files are ignored, if it is set to
235    * <code>false</code>, dot files are respected.
236    *
237    * @param ignoreDotFilesInBaseDir the flag to ignore files and directories
238    *          starting with a dot for checking modified files.
239    */
240   public void setIgnoreDotFilesInBaseDir(final boolean ignoreDotFilesInBaseDir)
241   {
242     this.ignoreDotFilesInBaseDir = ignoreDotFilesInBaseDir;
243   }
244 
245   // --- business -------------------------------------------------------------
246 
247   /**
248    * Returns the result of the change log query.
249    *
250    * @param repository the repository to fetch the change log information from.
251    * @param provider the provider to use to access the repository.
252    * @return the change log entries that match the query, <code>null</code> if
253    *         none have been found.
254    * @throws ScmException if the change log cannot be fetched.
255    */
256   public ChangeLogScmResult fetchChangeLog(final ScmRepository repository,
257       final ScmProvider provider) throws ScmException
258   {
259     try
260     {
261       ChangeLogScmResult result = null;
262       int currentRange = queryRangeInDays;
263       for (int i = 0; i <= DEFAULT_RETRY_COUNT; i++)
264       {
265         result =
266             provider.changeLog(repository, createFileSet(), null, null,
267                 currentRange, (ScmBranch) null, dateFormat);
268         if (!isEmpty(result))
269         {
270           return result;
271         }
272         currentRange += queryRangeInDays;
273       }
274       return result;
275     }
276     catch (final org.apache.maven.scm.ScmException e)
277     {
278       throw new ScmException("Cannot fetch change log from repository.", e);
279     }
280   }
281 
282   /**
283    * Checks if the given result contains change logs or not.
284    * <p>
285    * Calls
286    * {@link de.smartics.maven.plugin.buildmetadata.scm.maven.ScmAccessInfo#isEmpty(org.apache.maven.scm.command.changelog.ChangeLogSet)}
287    * with argument list (&lt;changeLogSet&gt;).
288    *
289    * @param result result the result to be checked.
290    * @return <code>true</code> if change logs have been found,<code>false</code>
291    *         if any reference up the path to the change logs is
292    *         <code>null</code> or the set is empty.
293    * @see de.smartics.maven.plugin.buildmetadata.scm.maven.ScmAccessInfo#isEmpty(org.apache.maven.scm.command.changelog.ChangeLogSet)
294    */
295   private boolean isEmpty(final ChangeLogScmResult result)
296   {
297     if (result != null)
298     {
299       final ChangeLogSet changeLogSet = result.getChangeLog();
300       if (changeLogSet != null)
301       {
302         return isEmpty(changeLogSet);
303       }
304     }
305     return false;
306   }
307 
308   private boolean isEmpty(final ChangeLogSet changeLogSet)
309   {
310     final List<?> changeLogSets = changeLogSet.getChangeSets();
311     if (changeLogSets != null)
312     {
313       return changeLogSets.isEmpty();
314     }
315 
316     return false;
317   }
318 
319   /**
320    * Creates the file set on the root directory of the checked out project.
321    *
322    * @return the file set on the root directory of the checked out project.
323    */
324   protected ScmFileSet createFileSet()
325   {
326     return new ScmFileSet(rootDirectory);
327   }
328 
329   /**
330    * Checks whether the SCM configuration calls for a failure due to changed
331    * files.
332    *
333    * @return <code>true</code> if a fail is indicated (i.e. there are locally
334    *         modified files that are to be considered for a check),
335    *         <code>false</code> if there are none.
336    */
337   public boolean isFailIndicated()
338   {
339     return isFailOnLocalModifications() && !isIgnoreDotFilesInBaseDir();
340   }
341 
342   // --- object basics --------------------------------------------------------
343 
344   /**
345    * Delegates call to {@link java.lang.StringBuilder#toString()}.
346    *
347    * @return the result of the call to
348    *         {@link java.lang.StringBuilder#toString()}.
349    * @see java.lang.StringBuilder#toString()
350    */
351   @Override
352   public String toString()
353   {
354     final StringBuilder buffer = new StringBuilder();
355 
356     buffer.append("SCM access info: rootDirectory=").append(rootDirectory);
357     appendIfExists(buffer, "dateFormat", dateFormat);
358     appendIfExists(buffer, "queryRangeInDays", String.valueOf(queryRangeInDays));
359     appendIfExists(buffer, "failOnLocalModifications",
360         String.valueOf(failOnLocalModifications));
361     appendIfExists(buffer, "ignoreDotFilesInBaseDir",
362         String.valueOf(ignoreDotFilesInBaseDir));
363 
364     return buffer.toString();
365   }
366 
367   private static void appendIfExists(final StringBuilder buffer,
368       final String label, final String value)
369   {
370     if (StringUtils.isNotBlank(value))
371     {
372       buffer.append(", ").append(label).append('=').append(value);
373     }
374   }
375 
376 }