You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RepositoryScannerInstance.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. package org.apache.archiva.repository.scanner;
  2. /*
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. */
  20. import org.apache.archiva.common.utils.BaseFile;
  21. import org.apache.archiva.common.utils.PathUtil;
  22. import org.apache.archiva.consumers.InvalidRepositoryContentConsumer;
  23. import org.apache.archiva.consumers.KnownRepositoryContentConsumer;
  24. import org.apache.archiva.consumers.functors.ConsumerWantsFilePredicate;
  25. import org.apache.archiva.repository.ManagedRepository;
  26. import org.apache.archiva.repository.scanner.functors.ConsumerProcessFileClosure;
  27. import org.apache.archiva.repository.scanner.functors.TriggerBeginScanClosure;
  28. import org.apache.archiva.repository.scanner.functors.TriggerScanCompletedClosure;
  29. import org.apache.commons.collections.Closure;
  30. import org.apache.commons.collections.CollectionUtils;
  31. import org.apache.commons.collections.functors.IfClosure;
  32. import org.apache.commons.lang.SystemUtils;
  33. import org.slf4j.Logger;
  34. import org.slf4j.LoggerFactory;
  35. import java.io.IOException;
  36. import java.nio.file.FileSystem;
  37. import java.nio.file.FileSystems;
  38. import java.nio.file.FileVisitResult;
  39. import java.nio.file.FileVisitor;
  40. import java.nio.file.Files;
  41. import java.nio.file.Path;
  42. import java.nio.file.PathMatcher;
  43. import java.nio.file.attribute.BasicFileAttributes;
  44. import java.util.ArrayList;
  45. import java.util.Date;
  46. import java.util.HashMap;
  47. import java.util.List;
  48. import java.util.Map;
  49. import java.util.stream.Collectors;
  50. /**
  51. * RepositoryScannerInstance
  52. */
  53. public class RepositoryScannerInstance
  54. implements FileVisitor<Path>
  55. {
  56. private Logger log = LoggerFactory.getLogger( RepositoryScannerInstance.class );
  57. /**
  58. * Consumers that process known content.
  59. */
  60. private List<KnownRepositoryContentConsumer> knownConsumers;
  61. /**
  62. * Consumers that process unknown/invalid content.
  63. */
  64. private List<InvalidRepositoryContentConsumer> invalidConsumers;
  65. private ManagedRepository repository;
  66. private RepositoryScanStatistics stats;
  67. private long changesSince = 0;
  68. private ConsumerProcessFileClosure consumerProcessFile;
  69. private ConsumerWantsFilePredicate consumerWantsFile;
  70. private Map<String, Long> consumerTimings;
  71. private Map<String, Long> consumerCounts;
  72. private List<String> fileNameIncludePattern = new ArrayList<>();
  73. private List<String> fileNameExcludePattern = new ArrayList<>();
  74. private List<PathMatcher> includeMatcher = new ArrayList<>();
  75. private List<PathMatcher> excludeMatcher = new ArrayList<>();
  76. private boolean isRunning = false;
  77. Path basePath = null;
  78. public RepositoryScannerInstance( ManagedRepository repository,
  79. List<KnownRepositoryContentConsumer> knownConsumerList,
  80. List<InvalidRepositoryContentConsumer> invalidConsumerList )
  81. {
  82. this.repository = repository;
  83. this.knownConsumers = knownConsumerList;
  84. this.invalidConsumers = invalidConsumerList;
  85. addFileNameIncludePattern("**/*");
  86. consumerTimings = new HashMap<>();
  87. consumerCounts = new HashMap<>();
  88. this.consumerProcessFile = new ConsumerProcessFileClosure();
  89. consumerProcessFile.setExecuteOnEntireRepo( true );
  90. consumerProcessFile.setConsumerTimings( consumerTimings );
  91. consumerProcessFile.setConsumerCounts( consumerCounts );
  92. this.consumerWantsFile = new ConsumerWantsFilePredicate( repository );
  93. stats = new RepositoryScanStatistics();
  94. stats.setRepositoryId( repository.getId() );
  95. Closure triggerBeginScan =
  96. new TriggerBeginScanClosure( repository, new Date( System.currentTimeMillis() ), true );
  97. CollectionUtils.forAllDo( knownConsumerList, triggerBeginScan );
  98. CollectionUtils.forAllDo( invalidConsumerList, triggerBeginScan );
  99. if ( SystemUtils.IS_OS_WINDOWS )
  100. {
  101. consumerWantsFile.setCaseSensitive( false );
  102. }
  103. }
  104. public RepositoryScannerInstance( ManagedRepository repository,
  105. List<KnownRepositoryContentConsumer> knownContentConsumers,
  106. List<InvalidRepositoryContentConsumer> invalidContentConsumers,
  107. long changesSince )
  108. {
  109. this( repository, knownContentConsumers, invalidContentConsumers );
  110. consumerWantsFile.setChangesSince( changesSince );
  111. this.changesSince = changesSince;
  112. }
  113. public RepositoryScanStatistics getStatistics()
  114. {
  115. return stats;
  116. }
  117. public Map<String, Long> getConsumerTimings()
  118. {
  119. return consumerTimings;
  120. }
  121. public Map<String, Long> getConsumerCounts()
  122. {
  123. return consumerCounts;
  124. }
  125. public ManagedRepository getRepository()
  126. {
  127. return repository;
  128. }
  129. public RepositoryScanStatistics getStats()
  130. {
  131. return stats;
  132. }
  133. public long getChangesSince()
  134. {
  135. return changesSince;
  136. }
  137. public List<String> getFileNameIncludePattern() {
  138. return fileNameIncludePattern;
  139. }
  140. public void setFileNameIncludePattern(List<String> fileNamePattern) {
  141. this.fileNameIncludePattern = fileNamePattern;
  142. FileSystem sys = FileSystems.getDefault();
  143. this.includeMatcher = fileNamePattern.stream().map(ts ->sys
  144. .getPathMatcher("glob:" + ts)).collect(Collectors.toList());
  145. }
  146. public void addFileNameIncludePattern(String fileNamePattern) {
  147. if (! this.fileNameIncludePattern.contains(fileNamePattern)) {
  148. this.fileNameIncludePattern.add(fileNamePattern);
  149. this.includeMatcher.add(FileSystems.getDefault().getPathMatcher("glob:" + fileNamePattern));
  150. }
  151. }
  152. public List<String> getFileNameExcludePattern() {
  153. return fileNameExcludePattern;
  154. }
  155. public void setFileNameExcludePattern(List<String> fileNamePattern) {
  156. this.fileNameExcludePattern = fileNamePattern;
  157. FileSystem sys = FileSystems.getDefault();
  158. this.excludeMatcher = fileNamePattern.stream().map(ts ->sys
  159. .getPathMatcher("glob:" + ts)).collect(Collectors.toList());
  160. }
  161. public void addFileNameExcludePattern(String fileNamePattern) {
  162. if (! this.fileNameExcludePattern.contains(fileNamePattern)) {
  163. this.fileNameExcludePattern.add(fileNamePattern);
  164. this.excludeMatcher.add(FileSystems.getDefault().getPathMatcher("glob:" + fileNamePattern));
  165. }
  166. }
  167. @Override
  168. public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
  169. if (!isRunning) {
  170. isRunning = true;
  171. this.basePath = dir;
  172. log.info( "Walk Started: [{}] {}", this.repository.getId(), this.repository.getLocation() );
  173. stats.triggerStart();
  174. }
  175. return FileVisitResult.CONTINUE;
  176. }
  177. @Override
  178. public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
  179. if (excludeMatcher.stream().noneMatch(m -> m.matches(file)) && includeMatcher.stream().allMatch(m -> m.matches(file))) {
  180. log.debug( "Walk Step: {}, {}", file );
  181. stats.increaseFileCount();
  182. // consume files regardless - the predicate will check the timestamp
  183. Path repoPath = PathUtil.getPathFromUri( repository.getLocation() );
  184. BaseFile basefile = new BaseFile( repoPath.toString(), file.toFile() );
  185. // Timestamp finished points to the last successful scan, not this current one.
  186. if ( Files.getLastModifiedTime(file).toMillis() >= changesSince )
  187. {
  188. stats.increaseNewFileCount();
  189. }
  190. consumerProcessFile.setBasefile( basefile );
  191. consumerWantsFile.setBasefile( basefile );
  192. Closure processIfWanted = IfClosure.getInstance( consumerWantsFile, consumerProcessFile );
  193. CollectionUtils.forAllDo( this.knownConsumers, processIfWanted );
  194. if ( consumerWantsFile.getWantedFileCount() <= 0 )
  195. {
  196. // Nothing known processed this file. It is invalid!
  197. CollectionUtils.forAllDo( this.invalidConsumers, consumerProcessFile );
  198. }
  199. }
  200. return FileVisitResult.CONTINUE;
  201. }
  202. @Override
  203. public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
  204. log.error("Error occured at {}: {}", file, exc.getMessage(), exc);
  205. if (basePath!=null && Files.isSameFile(file, basePath)) {
  206. finishWalk();
  207. }
  208. return FileVisitResult.CONTINUE;
  209. }
  210. @Override
  211. public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
  212. if (Files.isSameFile(dir, basePath)) {
  213. finishWalk();
  214. }
  215. return FileVisitResult.CONTINUE;
  216. }
  217. private void finishWalk() {
  218. this.isRunning = false;
  219. TriggerScanCompletedClosure scanCompletedClosure = new TriggerScanCompletedClosure( repository, true );
  220. CollectionUtils.forAllDo( knownConsumers, scanCompletedClosure );
  221. CollectionUtils.forAllDo( invalidConsumers, scanCompletedClosure );
  222. stats.setConsumerTimings( consumerTimings );
  223. stats.setConsumerCounts( consumerCounts );
  224. log.info( "Walk Finished: [{}] {}", this.repository.getId(), this.repository.getLocation() );
  225. stats.triggerFinished();
  226. this.basePath = null;
  227. }
  228. }