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 (<changeLogSet>). 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 }