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.

ManagedDefaultRepositoryContent.java 57KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352
  1. package org.apache.archiva.repository.maven.content;
  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. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. import org.apache.archiva.common.filelock.FileLockManager;
  20. import org.apache.archiva.common.utils.FileUtils;
  21. import org.apache.archiva.configuration.FileTypes;
  22. import org.apache.archiva.metadata.maven.MavenMetadataReader;
  23. import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
  24. import org.apache.archiva.model.ArchivaArtifact;
  25. import org.apache.archiva.model.ArtifactReference;
  26. import org.apache.archiva.model.ProjectReference;
  27. import org.apache.archiva.model.VersionedReference;
  28. import org.apache.archiva.repository.ContentAccessException;
  29. import org.apache.archiva.repository.ContentNotFoundException;
  30. import org.apache.archiva.repository.EditableManagedRepository;
  31. import org.apache.archiva.repository.LayoutException;
  32. import org.apache.archiva.repository.ManagedRepository;
  33. import org.apache.archiva.repository.ManagedRepositoryContent;
  34. import org.apache.archiva.repository.content.Artifact;
  35. import org.apache.archiva.repository.content.ArtifactType;
  36. import org.apache.archiva.repository.content.ContentItem;
  37. import org.apache.archiva.repository.content.ItemNotFoundException;
  38. import org.apache.archiva.repository.content.ItemSelector;
  39. import org.apache.archiva.repository.content.BaseArtifactTypes;
  40. import org.apache.archiva.repository.content.Namespace;
  41. import org.apache.archiva.repository.content.Project;
  42. import org.apache.archiva.repository.content.Version;
  43. import org.apache.archiva.repository.content.base.ArchivaItemSelector;
  44. import org.apache.archiva.repository.content.base.ArchivaNamespace;
  45. import org.apache.archiva.repository.content.base.ArchivaProject;
  46. import org.apache.archiva.repository.content.base.ArchivaVersion;
  47. import org.apache.archiva.repository.content.base.builder.ArtifactOptBuilder;
  48. import org.apache.archiva.repository.maven.metadata.storage.ArtifactMappingProvider;
  49. import org.apache.archiva.repository.maven.metadata.storage.DefaultArtifactMappingProvider;
  50. import org.apache.archiva.repository.storage.RepositoryStorage;
  51. import org.apache.archiva.repository.storage.StorageAsset;
  52. import org.apache.archiva.repository.storage.util.StorageUtil;
  53. import org.apache.commons.collections4.map.ReferenceMap;
  54. import org.apache.commons.lang3.StringUtils;
  55. import javax.inject.Inject;
  56. import javax.inject.Named;
  57. import java.io.IOException;
  58. import java.net.URI;
  59. import java.nio.file.Files;
  60. import java.nio.file.Path;
  61. import java.nio.file.Paths;
  62. import java.util.Arrays;
  63. import java.util.Collections;
  64. import java.util.List;
  65. import java.util.Objects;
  66. import java.util.Optional;
  67. import java.util.function.Predicate;
  68. import java.util.regex.Matcher;
  69. import java.util.regex.Pattern;
  70. import java.util.stream.Collectors;
  71. import java.util.stream.Stream;
  72. /**
  73. * ManagedDefaultRepositoryContent
  74. */
  75. public class ManagedDefaultRepositoryContent
  76. extends AbstractDefaultRepositoryContent
  77. implements ManagedRepositoryContent
  78. {
  79. // attribute flag that marks version objects that point to a snapshot artifact version
  80. public static final String SNAPSHOT_ARTIFACT_VERSION = "maven.snav";
  81. private FileTypes filetypes;
  82. public void setFileTypes(FileTypes fileTypes) {
  83. this.filetypes = fileTypes;
  84. }
  85. private ManagedRepository repository;
  86. private FileLockManager lockManager;
  87. @Inject
  88. @Named("repositoryPathTranslator#maven2")
  89. private RepositoryPathTranslator pathTranslator;
  90. @Inject
  91. @Named( "metadataReader#maven" )
  92. MavenMetadataReader metadataReader;
  93. @Inject
  94. @Named( "MavenContentHelper" )
  95. MavenContentHelper mavenContentHelper;
  96. public static final String SNAPSHOT = "SNAPSHOT";
  97. public static final Pattern UNIQUE_SNAPSHOT_PATTERN = Pattern.compile( "^(SNAPSHOT|[0-9]{8}\\.[0-9]{6}-[0-9]+)(.*)" );
  98. public static final Pattern CLASSIFIER_PATTERN = Pattern.compile( "^-([^.]+)(\\..*)" );
  99. public static final Pattern COMMON_EXTENSIONS = Pattern.compile( "^(jar|war|ear|dar|tar|zip|pom|xml)$" );
  100. public static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "^([0-9]{8})\\.([0-9]{6})$" );
  101. public static final Pattern GENERIC_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-" + SNAPSHOT );
  102. /**
  103. * We are caching content items in a weak reference map. To avoid always recreating the
  104. * the hierarchical structure.
  105. * TODO: Better use a object cache? E.g. our spring cache implementation?
  106. */
  107. private ReferenceMap<String, Namespace> namespaceMap = new ReferenceMap<>( );
  108. private ReferenceMap<StorageAsset, Project> projectMap = new ReferenceMap<>( );
  109. private ReferenceMap<StorageAsset, Version> versionMap = new ReferenceMap<>( );
  110. private ReferenceMap<StorageAsset, Artifact> artifactMap = new ReferenceMap<>( );
  111. public ManagedDefaultRepositoryContent() {
  112. super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
  113. }
  114. public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) {
  115. super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
  116. setFileTypes( fileTypes );
  117. this.lockManager = lockManager;
  118. setRepository( repository );
  119. }
  120. public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
  121. {
  122. super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders);
  123. setFileTypes( fileTypes );
  124. this.lockManager = lockManager;
  125. setRepository( repository );
  126. }
  127. private StorageAsset getAssetByPath(String assetPath) {
  128. return getStorage( ).getAsset( assetPath );
  129. }
  130. private StorageAsset getAsset(String namespace) {
  131. String namespacePath = formatAsDirectory( namespace.trim() );
  132. if (StringUtils.isEmpty( namespacePath )) {
  133. namespacePath = "";
  134. }
  135. return getAssetByPath(namespacePath);
  136. }
  137. private StorageAsset getAsset(String namespace, String project) {
  138. return getAsset( namespace ).resolve( project );
  139. }
  140. private StorageAsset getAsset(String namespace, String project, String version) {
  141. return getAsset( namespace, project ).resolve( version );
  142. }
  143. private StorageAsset getAsset(String namespace, String project, String version, String fileName) {
  144. return getAsset( namespace, project, version ).resolve( fileName );
  145. }
  146. /// ************* Start of new generation interface ******************
  147. /**
  148. * Removes the item from the filesystem. For namespaces, projects and versions it deletes
  149. * recursively.
  150. * For namespaces you have to be careful, because maven repositories may have sub namespaces
  151. * parallel to projects. Which means deleting a namespaces also deletes the sub namespaces and
  152. * not only the projects of the given namespace. Better run the delete for each project of
  153. * a namespace.
  154. *
  155. * Artifacts are deleted as provided. No related artifacts will be deleted.
  156. *
  157. * @param item the item that should be removed
  158. * @throws ItemNotFoundException if the item does not exist
  159. * @throws ContentAccessException if some error occurred while accessing the filesystem
  160. */
  161. @Override
  162. public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
  163. {
  164. final Path baseDirectory = getRepoDir( );
  165. final Path itemPath = item.getAsset( ).getFilePath( );
  166. if ( !Files.exists( itemPath ) )
  167. {
  168. throw new ItemNotFoundException( "The item " + item.toString() + "does not exist in the repository " + getId( ) );
  169. }
  170. if ( !itemPath.toAbsolutePath().startsWith( baseDirectory.toAbsolutePath() ) )
  171. {
  172. log.error( "The namespace {} to delete from repository {} is not a subdirectory of the repository base.", item, getId( ) );
  173. log.error( "Namespace directory: {}", itemPath );
  174. log.error( "Repository directory: {}", baseDirectory );
  175. throw new ContentAccessException( "Inconsistent directories found. Could not delete namespace." );
  176. }
  177. try
  178. {
  179. if (Files.isDirectory( itemPath ))
  180. {
  181. FileUtils.deleteDirectory( itemPath );
  182. } else {
  183. Files.deleteIfExists( itemPath );
  184. }
  185. }
  186. catch ( IOException e )
  187. {
  188. log.error( "Could not delete item from path {}: {}", itemPath, e.getMessage( ), e );
  189. throw new ContentAccessException( "Error occured while deleting item " + item + ": " + e.getMessage( ), e );
  190. }
  191. }
  192. @Override
  193. public ContentItem getItem( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
  194. {
  195. if (selector.hasVersion() && selector.hasArtifactId()) {
  196. return getArtifact( selector );
  197. } else if (selector.hasProjectId() && selector.hasVersion()) {
  198. return getVersion( selector );
  199. } else if (selector.hasProjectId()) {
  200. return getProject( selector );
  201. } else {
  202. return getNamespace( selector );
  203. }
  204. }
  205. @Override
  206. public Namespace getNamespace( final ItemSelector namespaceSelector ) throws ContentAccessException, IllegalArgumentException
  207. {
  208. return namespaceMap.computeIfAbsent( namespaceSelector.getNamespace(),
  209. namespace -> {
  210. StorageAsset nsPath = getAsset( namespace );
  211. return ArchivaNamespace.withRepository( this ).withAsset( nsPath ).
  212. withNamespace( namespace ).build( );
  213. });
  214. }
  215. @Override
  216. public Project getProject( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
  217. {
  218. if (!selector.hasProjectId()) {
  219. throw new IllegalArgumentException( "Project id must be set" );
  220. }
  221. final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
  222. return projectMap.computeIfAbsent( path, projectPath -> {
  223. final Namespace ns = getNamespace( selector );
  224. return ArchivaProject.withAsset( projectPath ).withNamespace( ns ).withId( selector.getProjectId( ) ).build( );
  225. }
  226. );
  227. }
  228. @Override
  229. public Version getVersion( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
  230. {
  231. if (!selector.hasProjectId()) {
  232. throw new IllegalArgumentException( "Project id must be set" );
  233. }
  234. if (!selector.hasVersion() ) {
  235. throw new IllegalArgumentException( "Version must be set" );
  236. }
  237. final StorageAsset path = getAsset(selector.getNamespace(), selector.getProjectId(), selector.getVersion());
  238. return versionMap.computeIfAbsent( path, versionPath -> {
  239. final Project project = getProject( selector );
  240. return ArchivaVersion.withAsset( path )
  241. .withProject( project )
  242. .withVersion( selector.getVersion( ) ).build();
  243. } );
  244. }
  245. public Artifact createArtifact(final StorageAsset artifactPath, final ItemSelector selector,
  246. final String classifier, final String extension) {
  247. Version version = getVersion(selector);
  248. ArtifactOptBuilder builder = org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
  249. .withVersion( version )
  250. .withId( selector.getArtifactId( ) )
  251. .withArtifactVersion( mavenContentHelper.getArtifactVersion( artifactPath, selector ) )
  252. .withClassifier( classifier );
  253. if (selector.hasType()) {
  254. builder.withType( selector.getType( ) );
  255. }
  256. return builder.build( );
  257. }
  258. public Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath) {
  259. final StorageAsset namespacePath = artifactPath.getParent( ).getParent( ).getParent( );
  260. final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
  261. return namespaceMap.computeIfAbsent( namespace,
  262. myNamespace -> ArchivaNamespace.withRepository( this )
  263. .withAsset( namespacePath )
  264. .withNamespace( namespace )
  265. .build( ) );
  266. }
  267. public Namespace getNamespaceFromPath( final StorageAsset namespacePath) {
  268. final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
  269. return namespaceMap.computeIfAbsent( namespace,
  270. myNamespace -> ArchivaNamespace.withRepository( this )
  271. .withAsset( namespacePath )
  272. .withNamespace( namespace )
  273. .build( ) );
  274. }
  275. private Project getProjectFromPath( final StorageAsset projectPath) {
  276. return projectMap.computeIfAbsent( projectPath,
  277. myProjectPath -> ArchivaProject.withAsset( projectPath )
  278. .withNamespace( getNamespaceFromPath( projectPath.getParent() ) )
  279. .withId( projectPath.getName( ) ).build( )
  280. );
  281. }
  282. private Project getProjectFromArtifactPath( final StorageAsset artifactPath) {
  283. final StorageAsset projectPath = artifactPath.getParent( ).getParent( );
  284. return projectMap.computeIfAbsent( projectPath,
  285. myProjectPath -> ArchivaProject.withAsset( projectPath )
  286. .withNamespace( getNamespaceFromArtifactPath( artifactPath ) )
  287. .withId( projectPath.getName( ) ).build( )
  288. );
  289. }
  290. private Version getVersionFromArtifactPath( final StorageAsset artifactPath) {
  291. final StorageAsset versionPath = artifactPath.getParent( );
  292. return versionMap.computeIfAbsent( versionPath,
  293. myVersionPath -> ArchivaVersion.withAsset( versionPath )
  294. .withProject( getProjectFromArtifactPath( artifactPath ) )
  295. .withVersion( versionPath.getName( ) ).build( ) );
  296. }
  297. private Artifact getArtifactFromPath(final StorageAsset artifactPath) {
  298. final Version version = getVersionFromArtifactPath( artifactPath );
  299. final ArtifactInfo info = getArtifactInfoFromPath( version.getVersion(), artifactPath );
  300. return artifactMap.computeIfAbsent( artifactPath, myArtifactPath ->
  301. org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
  302. .withVersion( version )
  303. .withId( info.id )
  304. .withClassifier( info.classifier )
  305. .withRemainder( info.remainder )
  306. .withType( info.type )
  307. .withArtifactVersion( info.version )
  308. .withContentType( info.contentType )
  309. .withArtifactType( info.artifactType )
  310. .build( )
  311. );
  312. }
  313. private ContentItem getItemFromPath(final StorageAsset itemPath) {
  314. if (itemPath.isLeaf()) {
  315. return getArtifactFromPath( itemPath );
  316. } else {
  317. if (versionMap.containsKey( itemPath )) {
  318. return versionMap.get( itemPath );
  319. }
  320. if (projectMap.containsKey( itemPath )) {
  321. return projectMap.get( itemPath );
  322. }
  323. String ns = MavenContentHelper.getNamespaceFromNamespacePath( itemPath );
  324. if (namespaceMap.containsKey( ns )) {
  325. return namespaceMap.get( ns );
  326. }
  327. // No cached item, so we have to gather more information:
  328. // Check for version directory (contains at least a pom or metadata file)
  329. if (itemPath.list( ).stream( ).map(a -> a.getName().toLowerCase()).anyMatch( n ->
  330. n.endsWith( ".pom" )
  331. )) {
  332. return versionMap.computeIfAbsent( itemPath,
  333. myVersionPath -> ArchivaVersion.withAsset( itemPath )
  334. .withProject( (Project)getItemFromPath( itemPath.getParent() ) )
  335. .withVersion( itemPath.getName() ).build());
  336. } else {
  337. // We have to dig further and find the next directory with a pom
  338. Optional<StorageAsset> foundFile = StorageUtil.newAssetStream( itemPath )
  339. .filter( a -> a.getName().toLowerCase().endsWith( ".pom" )
  340. || a.getName().toLowerCase().startsWith( "maven-metadata" ) )
  341. .findFirst( );
  342. if (foundFile.isPresent())
  343. {
  344. int level = 0;
  345. StorageAsset current = foundFile.get( );
  346. while (current.hasParent() && !current.equals(itemPath)) {
  347. level++;
  348. current = current.getParent( );
  349. }
  350. // Project path if it is one level up from the found file
  351. if (level==2) {
  352. return projectMap.computeIfAbsent( itemPath,
  353. myItemPath -> getProjectFromArtifactPath( foundFile.get( ) ) );
  354. } else {
  355. // All other paths are treated as namespace
  356. return namespaceMap.computeIfAbsent( ns,
  357. myNamespace -> ArchivaNamespace.withRepository( this )
  358. .withAsset( itemPath )
  359. .withNamespace( ns )
  360. .build( ) );
  361. }
  362. } else {
  363. // Don't know what to do with it, so we treat it as namespace path
  364. return namespaceMap.computeIfAbsent( ns,
  365. myNamespace -> ArchivaNamespace.withRepository( this )
  366. .withAsset( itemPath )
  367. .withNamespace( ns )
  368. .build( ) );
  369. }
  370. }
  371. }
  372. }
  373. // Simple object to hold artifact information
  374. private class ArtifactInfo {
  375. private String id;
  376. private String version;
  377. private String extension;
  378. private String remainder;
  379. private String type;
  380. private String classifier;
  381. private String contentType;
  382. private StorageAsset asset;
  383. private ArtifactType artifactType = BaseArtifactTypes.MAIN;
  384. }
  385. private ArtifactInfo getArtifactInfoFromPath(String genericVersion, StorageAsset path) {
  386. final ArtifactInfo info = new ArtifactInfo( );
  387. info.asset = path;
  388. info.id = path.getParent( ).getParent( ).getName( );
  389. final String fileName = path.getName( );
  390. if ( genericVersion.endsWith( "-" + SNAPSHOT ) )
  391. {
  392. String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT );
  393. String prefix = info.id+"-"+baseVersion+"-";
  394. if (fileName.startsWith( prefix ))
  395. {
  396. String versionPostfix = StringUtils.removeStart( fileName, prefix );
  397. Matcher matcher = UNIQUE_SNAPSHOT_PATTERN.matcher( versionPostfix );
  398. if (matcher.matches()) {
  399. info.version = baseVersion + "-" + matcher.group( 1 );
  400. String newPrefix = info.id + "-" + info.version;
  401. if (fileName.startsWith( newPrefix ))
  402. {
  403. String classPostfix = StringUtils.removeStart( fileName, newPrefix );
  404. Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
  405. if (cMatch.matches()) {
  406. info.classifier = cMatch.group( 1 );
  407. info.remainder = cMatch.group( 2 );
  408. } else {
  409. info.classifier = "";
  410. info.remainder = classPostfix;
  411. }
  412. } else {
  413. log.debug( "Artifact does not match the maven name pattern {}", path );
  414. info.artifactType = BaseArtifactTypes.UNKNOWN;
  415. info.classifier = "";
  416. info.remainder = StringUtils.substringAfter( fileName, prefix );
  417. }
  418. } else {
  419. log.debug( "Artifact does not match the snapshot version pattern {}", path );
  420. info.artifactType = BaseArtifactTypes.UNKNOWN;
  421. // This is just a guess. No guarantee to the get a usable version.
  422. info.version = StringUtils.removeStart( fileName, info.id + '-' );
  423. String postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase();
  424. while (COMMON_EXTENSIONS.matcher(postfix).matches()) {
  425. info.version = StringUtils.substringBeforeLast( info.version, "." );
  426. postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase();
  427. }
  428. info.classifier = "";
  429. info.remainder = StringUtils.substringAfter( fileName, prefix );
  430. }
  431. } else {
  432. log.debug( "Artifact does not match the maven name pattern: {}", path );
  433. if ( fileName.contains( "-"+baseVersion ) )
  434. {
  435. info.id = StringUtils.substringBefore( fileName, "-"+baseVersion );
  436. } else {
  437. info.id = fileName;
  438. }
  439. info.artifactType = BaseArtifactTypes.UNKNOWN;
  440. info.version = "";
  441. info.classifier = "";
  442. info.remainder = StringUtils.substringAfterLast( fileName, "." );
  443. }
  444. } else {
  445. String prefix = info.id+"-"+genericVersion;
  446. if (fileName.startsWith( prefix ))
  447. {
  448. info.version=genericVersion;
  449. String classPostfix = StringUtils.removeStart( fileName, prefix );
  450. Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
  451. if (cMatch.matches()) {
  452. info.classifier = cMatch.group( 1 );
  453. info.remainder = cMatch.group( 2 );
  454. } else {
  455. info.classifier = "";
  456. info.remainder = classPostfix;
  457. }
  458. } else {
  459. if (fileName.contains( "-"+genericVersion )) {
  460. info.id = StringUtils.substringBefore( fileName, "-"+genericVersion );
  461. } else {
  462. info.id = fileName;
  463. }
  464. log.debug( "Artifact does not match the version pattern {}", path );
  465. info.artifactType = BaseArtifactTypes.UNKNOWN;
  466. info.version = "";
  467. info.classifier = "";
  468. info.remainder = StringUtils.substringAfterLast( fileName, "." );
  469. }
  470. }
  471. info.extension = StringUtils.substringAfterLast( fileName, "." );
  472. info.type = MavenContentHelper.getTypeFromClassifierAndExtension( info.classifier, info.extension );
  473. try {
  474. info.contentType = Files.probeContentType( path.getFilePath( ) );
  475. } catch (IOException e) {
  476. info.contentType = "";
  477. //
  478. }
  479. if (MavenContentHelper.METADATA_FILENAME.equalsIgnoreCase( fileName )) {
  480. info.artifactType = BaseArtifactTypes.METADATA;
  481. } else if (MavenContentHelper.METADATA_REPOSITORY_FILENAME.equalsIgnoreCase( fileName )) {
  482. info.artifactType = MavenTypes.REPOSITORY_METADATA;
  483. } else if (StringUtils.isNotEmpty( info.remainder ) && StringUtils.countMatches( info.remainder, "." )>=2) {
  484. String mainFile = StringUtils.substringBeforeLast( fileName, "." );
  485. if (path.getParent().resolve( mainFile ).exists())
  486. {
  487. info.artifactType = BaseArtifactTypes.RELATED;
  488. }
  489. }
  490. return info;
  491. }
  492. @Override
  493. public Artifact getArtifact( final ItemSelector selector ) throws ContentAccessException
  494. {
  495. if (!selector.hasProjectId( )) {
  496. throw new IllegalArgumentException( "Project id must be set" );
  497. }
  498. if (!selector.hasVersion( )) {
  499. throw new IllegalArgumentException( "Version must be set" );
  500. }
  501. if (!selector.hasArtifactId( )) {
  502. throw new IllegalArgumentException( "Artifact id must be set" );
  503. }
  504. final StorageAsset artifactDir = getAsset(selector.getNamespace(), selector.getProjectId(),
  505. selector.getVersion());
  506. final String artifactVersion = mavenContentHelper.getArtifactVersion( artifactDir, selector );
  507. final String classifier = MavenContentHelper.getClassifier( selector );
  508. final String extension = MavenContentHelper.getArtifactExtension( selector );
  509. final String artifactId = StringUtils.isEmpty( selector.getArtifactId( ) ) ? selector.getProjectId( ) : selector.getArtifactId( );
  510. final String fileName = MavenContentHelper.getArtifactFileName( artifactId, artifactVersion, classifier, extension );
  511. final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ),
  512. selector.getVersion( ), fileName );
  513. return artifactMap.computeIfAbsent( path, artifactPath -> createArtifact( path, selector, classifier, extension ) );
  514. }
  515. /**
  516. * Returns all the subdirectories of the given namespace directory as project.
  517. */
  518. @Override
  519. public List<? extends Project> getProjects( Namespace namespace )
  520. {
  521. return namespace.getAsset( ).list( ).stream( )
  522. .filter( a -> a.isContainer( ) )
  523. .map( a -> getProjectFromPath( a ) )
  524. .collect( Collectors.toList());
  525. }
  526. @Override
  527. public List<? extends Project> getProjects( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
  528. {
  529. return getProjects( getNamespace( selector ) );
  530. }
  531. /**
  532. * Returns a version object for each directory that is a direct child of the project directory.
  533. * @param project the project for which the versions should be returned
  534. * @return the list of versions or a empty list, if not version was found
  535. */
  536. @Override
  537. public List<? extends Version> getVersions( final Project project )
  538. {
  539. StorageAsset asset = getAsset( project.getNamespace( ).getNamespace( ), project.getId( ) );
  540. return asset.list( ).stream( ).filter( a -> a.isContainer( ) )
  541. .map( a -> ArchivaVersion.withAsset( a )
  542. .withProject( project )
  543. .withVersion( a.getName() ).build() )
  544. .collect( Collectors.toList( ) );
  545. }
  546. /**
  547. * If the selector specifies a version, all artifact versions are returned, which means for snapshot
  548. * versions the artifact versions are returned too.
  549. *
  550. * @param selector the item selector. At least namespace and projectId must be set.
  551. * @return the list of version objects or a empty list, if the selector does not match a version
  552. * @throws ContentAccessException if the access to the underlying backend failed
  553. * @throws IllegalArgumentException if the selector has no projectId specified
  554. */
  555. @Override
  556. public List<? extends Version> getVersions( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
  557. {
  558. if (!selector.hasProjectId()) {
  559. log.error( "Bad item selector for version list: {}", selector );
  560. throw new IllegalArgumentException( "Project id not set, while retrieving versions." );
  561. }
  562. final Project project = getProject( selector );
  563. if (selector.hasVersion()) {
  564. final StorageAsset asset = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion( ) );
  565. return asset.list( ).stream( ).map( a -> getArtifactInfoFromPath( selector.getVersion( ), a ) )
  566. .filter( ai -> StringUtils.isNotEmpty( ai.version ) )
  567. .map( v -> ArchivaVersion.withAsset( v.asset.getParent() )
  568. .withProject( project ).withVersion( v.version )
  569. .withAttribute(SNAPSHOT_ARTIFACT_VERSION,"true").build() )
  570. .distinct()
  571. .collect( Collectors.toList( ) );
  572. } else {
  573. return getVersions( project );
  574. }
  575. }
  576. /**
  577. * See {@link #newArtifactStream(ItemSelector)}. This method collects the stream into a list.
  578. *
  579. * @param selector the selector for the artifacts
  580. * @return the list of artifacts
  581. * @throws ContentAccessException if the access to the underlying filesystem failed
  582. */
  583. @Override
  584. public List<? extends Artifact> getArtifacts( ItemSelector selector ) throws ContentAccessException
  585. {
  586. try(Stream<? extends Artifact> stream = newArtifactStream( selector )) {
  587. return stream.collect( Collectors.toList());
  588. }
  589. }
  590. /*
  591. * File filter to select certain artifacts using the selector data.
  592. */
  593. private Predicate<StorageAsset> getFileFilterFromSelector(final ItemSelector selector) {
  594. Predicate<StorageAsset> p = a -> a.isLeaf( );
  595. StringBuilder fileNamePattern = new StringBuilder("^" );
  596. if (selector.hasArtifactId()) {
  597. fileNamePattern.append( Pattern.quote(selector.getArtifactId( )) ).append("-");
  598. } else {
  599. fileNamePattern.append("[A-Za-z0-9_\\-.]+-");
  600. }
  601. if (selector.hasArtifactVersion()) {
  602. if ( selector.getArtifactVersion( ).contains("*")) {
  603. String[] tokens = StringUtils.splitByWholeSeparator( selector.getArtifactVersion( ), "*" );
  604. for (String currentToken : tokens) {
  605. if (!currentToken.equals("")) {
  606. fileNamePattern.append( Pattern.quote( currentToken ) );
  607. }
  608. fileNamePattern.append( "[A-Za-z0-9_\\-.]*" );
  609. }
  610. } else
  611. {
  612. fileNamePattern.append( Pattern.quote( selector.getArtifactVersion( ) ) );
  613. }
  614. } else {
  615. fileNamePattern.append( "[A-Za-z0-9_\\-.]+" );
  616. }
  617. String classifier = selector.hasClassifier( ) ? selector.getClassifier( ) :
  618. ( selector.hasType( ) ? MavenContentHelper.getClassifierFromType( selector.getType( ) ) : null );
  619. if (classifier != null)
  620. {
  621. if ( "*".equals( classifier ) )
  622. {
  623. fileNamePattern.append( "(-[A-Za-z0-9]+)?\\." );
  624. }
  625. else
  626. {
  627. fileNamePattern.append("-").append( Pattern.quote( classifier ) ).append( "\\." );
  628. }
  629. } else {
  630. fileNamePattern.append( "\\." );
  631. }
  632. String extension = selector.hasExtension( ) ? selector.getExtension( ) :
  633. ( selector.hasType( ) ? MavenContentHelper.getArtifactExtension( selector ) : null );
  634. if (extension != null) {
  635. if (selector.includeRelatedArtifacts())
  636. {
  637. fileNamePattern.append( Pattern.quote( extension ) ).append("(\\.[A-Za-z0-9]+)?");
  638. } else {
  639. fileNamePattern.append( Pattern.quote( extension ) );
  640. }
  641. } else {
  642. fileNamePattern.append( "[A-Za-z0-9.]+" );
  643. }
  644. final Pattern pattern = Pattern.compile( fileNamePattern.toString() );
  645. return p.and( a -> pattern.matcher( a.getName( ) ).matches());
  646. }
  647. /**
  648. * Returns the artifacts. The number of artifacts returned depend on the selector.
  649. * If the selector sets the flag {@link ItemSelector#includeRelatedArtifacts()} to <code>true</code>,
  650. * additional to the matching artifacts, related artifacts like hash values or signatures are included in the artifact
  651. * stream.
  652. * If the selector sets the flag {@link ItemSelector#recurse()} to <code>true</code>, artifacts of the given
  653. * namespace and from all sub namespaces that start with the given namespace are returned.
  654. * <ul>
  655. * <li>If only a namespace is given, all artifacts with the given namespace or starting with the given
  656. * namespace (see {@link ItemSelector#recurse()} are returned.</li>
  657. * <li>If a namespace and a project id, or artifact id is given, the artifacts of all versions of the given
  658. * namespace and project are returned.</li>
  659. * <li>If a namespace and a project id or artifact id and a version is given, the artifacts of the given
  660. * version are returned</li>
  661. * <li>If no artifact version or artifact id is given, it will return all "artifacts" found in the directory.
  662. * To select only artifacts that match the layout you should add the artifact id and artifact version
  663. * (can contain a '*' pattern).</li>
  664. * </ul>
  665. *
  666. * The '*' pattern can be used in classifiers and artifact versions and match zero or more characters.
  667. *
  668. * There is no determinate order of the elements in the stream.
  669. *
  670. * Returned streams are auto closable and should be used in a try-with-resources statement.
  671. *
  672. * @param selector the item selector
  673. * @throws ContentAccessException if the access to the underlying filesystem failed
  674. */
  675. @Override
  676. public Stream<? extends Artifact> newArtifactStream( ItemSelector selector ) throws ContentAccessException
  677. {
  678. String projectId = selector.hasProjectId( ) ? selector.getProjectId( ) : ( selector.hasArtifactId( ) ? selector.getArtifactId( )
  679. : null );
  680. final Predicate<StorageAsset> filter = getFileFilterFromSelector( selector );
  681. if (projectId!=null && selector.hasVersion()) {
  682. return getAsset( selector.getNamespace( ), projectId, selector.getVersion( ) )
  683. .list( ).stream( ).filter( filter )
  684. .map( this::getArtifactFromPath );
  685. } else if (projectId!=null) {
  686. final StorageAsset projDir = getAsset( selector.getNamespace( ), projectId );
  687. return projDir.list( ).stream( )
  688. .map(a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
  689. .flatMap( List::stream )
  690. .filter( filter )
  691. .map( this::getArtifactFromPath );
  692. } else
  693. {
  694. StorageAsset namespaceDir = getAsset( selector.getNamespace( ) );
  695. if (selector.recurse())
  696. {
  697. return StorageUtil.newAssetStream( namespaceDir, true )
  698. .filter( filter )
  699. .map( this::getArtifactFromPath );
  700. } else {
  701. // We descend into 2 subdirectories (project and version)
  702. return namespaceDir.list( ).stream( )
  703. .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
  704. .flatMap( List::stream )
  705. .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
  706. .flatMap( List::stream )
  707. .filter( filter )
  708. .map( this::getArtifactFromPath );
  709. }
  710. }
  711. }
  712. /**
  713. * Same as {@link #newArtifactStream(ContentItem)} but returns the collected stream as list.
  714. *
  715. * @param item the item the parent item
  716. * @return the list of artifacts or a empty list of no artifacts where found
  717. */
  718. @Override
  719. public List<? extends Artifact> getArtifacts( ContentItem item )
  720. {
  721. try(Stream<? extends Artifact> stream = newArtifactStream( item )) {
  722. return stream.collect( Collectors.toList());
  723. }
  724. }
  725. /**
  726. * Returns all artifacts
  727. * @param item
  728. * @return
  729. * @throws ContentAccessException
  730. */
  731. public Stream<? extends Artifact> newArtifactStream( Namespace item ) throws ContentAccessException
  732. {
  733. return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getNamespace( ) ).build( ) );
  734. }
  735. public Stream<? extends Artifact> newArtifactStream( Project item ) throws ContentAccessException
  736. {
  737. return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getNamespace( ).getNamespace() )
  738. .withProjectId( item.getId() ).build( ) );
  739. }
  740. public Stream<? extends Artifact> newArtifactStream( Version item ) throws ContentAccessException
  741. {
  742. return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getProject().getNamespace( ).getNamespace() )
  743. .withProjectId( item.getProject().getId() )
  744. .withVersion( item.getVersion() ).build( ) );
  745. }
  746. /**
  747. * Returns all related artifacts that match the given artifact. That means all artifacts that have
  748. * the same filename plus an additional extension, e.g. ${fileName}.sha2
  749. * @param item the artifact
  750. * @return the stream of artifacts
  751. * @throws ContentAccessException
  752. */
  753. public Stream<? extends Artifact> newArtifactStream( Artifact item ) throws ContentAccessException
  754. {
  755. final Version v = item.getVersion( );
  756. final String fileName = item.getFileName( );
  757. final Predicate<StorageAsset> filter = ( StorageAsset a ) ->
  758. a.getName( ).startsWith( fileName + "." );
  759. return v.getAsset( ).list( ).stream( ).filter( filter )
  760. .map( a -> getArtifactFromPath( a ) );
  761. }
  762. /**
  763. * Returns the stream of artifacts that are children of the given item.
  764. *
  765. * @param item the item from where the artifacts should be returned
  766. * @return
  767. * @throws ContentAccessException
  768. */
  769. @Override
  770. public Stream<? extends Artifact> newArtifactStream( ContentItem item ) throws ContentAccessException
  771. {
  772. if (item instanceof Namespace) {
  773. return newArtifactStream( ( (Namespace) item ) );
  774. } else if (item instanceof Project) {
  775. return newArtifactStream( (Project) item );
  776. } else if (item instanceof Version) {
  777. return newArtifactStream( (Version) item );
  778. } else if (item instanceof Artifact) {
  779. return newArtifactStream( (Artifact) item );
  780. } else
  781. {
  782. log.warn( "newArtifactStream for unsupported item requested: {}", item.getClass( ).getName( ) );
  783. return Stream.empty( );
  784. }
  785. }
  786. /**
  787. * Checks, if the asset/file queried by the given selector exists.
  788. */
  789. @Override
  790. public boolean hasContent( ItemSelector selector )
  791. {
  792. return getItem( selector ).getAsset( ).exists( );
  793. }
  794. /**
  795. * Moves the file to the artifact destination
  796. */
  797. @Override
  798. public void addArtifact( Path sourceFile, Artifact destination ) throws IllegalArgumentException, ContentAccessException
  799. {
  800. try
  801. {
  802. StorageAsset asset = destination.getAsset( );
  803. if (!asset.exists()) {
  804. asset.create();
  805. }
  806. asset.replaceDataFromFile( sourceFile );
  807. }
  808. catch ( IOException e )
  809. {
  810. log.error( "Could not push data to asset source={} destination={}. {}", sourceFile, destination.getAsset().getFilePath(), e.getMessage( ) );
  811. throw new ContentAccessException( e.getMessage( ), e );
  812. }
  813. }
  814. @Override
  815. public ContentItem toItem( String path ) throws LayoutException
  816. {
  817. StorageAsset asset = getRepository( ).getAsset( path );
  818. if (asset.isLeaf())
  819. {
  820. ItemSelector selector = getPathParser( ).toItemSelector( path );
  821. return getItem( selector );
  822. } else {
  823. return getItemFromPath( asset );
  824. }
  825. }
  826. @Override
  827. public ContentItem toItem( StorageAsset assetPath ) throws LayoutException
  828. {
  829. return toItem( assetPath.getPath( ) );
  830. }
  831. /// ************* End of new generation interface ******************
  832. /**
  833. * Returns a version reference from the coordinates
  834. * @param groupId the group id
  835. * @param artifactId the artifact id
  836. * @param version the version
  837. * @return the versioned reference object
  838. */
  839. @Override
  840. public VersionedReference toVersion( String groupId, String artifactId, String version ) {
  841. return new VersionedReference().groupId( groupId ).artifactId( artifactId ).version( version );
  842. }
  843. /**
  844. * Return the version the artifact is part of
  845. * @param artifactReference
  846. * @return
  847. */
  848. public VersionedReference toVersion( ArtifactReference artifactReference) {
  849. return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), artifactReference.getVersion( ) );
  850. }
  851. @Override
  852. public void deleteVersion( VersionedReference ref ) throws ContentNotFoundException, ContentAccessException
  853. {
  854. final String path = toPath( ref );
  855. final Path deleteTarget = getRepoDir().resolve(path);
  856. if ( !Files.exists(deleteTarget) )
  857. {
  858. log.warn( "Version path for repository {} does not exist: {}", getId(), deleteTarget );
  859. throw new ContentNotFoundException( "Version not found for repository "+getId()+": "+path );
  860. }
  861. if ( Files.isDirectory(deleteTarget) )
  862. {
  863. try
  864. {
  865. org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
  866. }
  867. catch ( IOException e )
  868. {
  869. log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
  870. throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
  871. }
  872. } else {
  873. log.warn( "Version path for repository {} is not a directory {}", getId(), deleteTarget );
  874. throw new ContentNotFoundException( "Version path for repository "+getId()+" is not directory: " + path );
  875. }
  876. }
  877. @Override
  878. public void deleteProject( ProjectReference ref )
  879. throws ContentNotFoundException, ContentAccessException
  880. {
  881. final String path = toPath( ref );
  882. final Path deleteTarget = getRepoDir( ).resolve( path );
  883. if ( !Files.exists(deleteTarget) )
  884. {
  885. log.warn( "Project path for repository {} does not exist: {}", getId(), deleteTarget );
  886. throw new ContentNotFoundException( "Project not found for repository "+getId()+": "+path );
  887. }
  888. if ( Files.isDirectory(deleteTarget) )
  889. {
  890. try
  891. {
  892. org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
  893. }
  894. catch ( IOException e )
  895. {
  896. log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
  897. throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
  898. }
  899. }
  900. else
  901. {
  902. log.warn( "Project path for repository {} is not a directory {}", getId(), deleteTarget );
  903. throw new ContentNotFoundException( "Project path for repository "+getId()+" is not directory: " + path );
  904. }
  905. }
  906. @Override
  907. public void deleteProject( String namespace, String projectId ) throws ContentNotFoundException, ContentAccessException
  908. {
  909. this.deleteProject( new ProjectReference().groupId( namespace ).artifactId( projectId ) );
  910. }
  911. @Override
  912. public void deleteArtifact( ArtifactReference ref ) throws ContentNotFoundException, ContentAccessException
  913. {
  914. final String path = toPath( ref );
  915. final Path repoDir = getRepoDir( );
  916. Path deleteTarget = repoDir.resolve( path );
  917. if ( Files.exists(deleteTarget) )
  918. {
  919. try
  920. {
  921. if (Files.isDirectory( deleteTarget ))
  922. {
  923. org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
  924. } else {
  925. Files.delete( deleteTarget );
  926. }
  927. }
  928. catch ( IOException e )
  929. {
  930. log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
  931. throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
  932. }
  933. } else {
  934. log.warn( "Artifact path for repository {} does not exist: {}", getId(), deleteTarget );
  935. throw new ContentNotFoundException( "Artifact not found for repository "+getId()+": "+path );
  936. }
  937. }
  938. @Override
  939. public void deleteGroupId( String groupId )
  940. throws ContentNotFoundException, ContentAccessException
  941. {
  942. final String path = toPath( groupId );
  943. final Path deleteTarget = getRepoDir( ).resolve( path );
  944. if (!Files.exists(deleteTarget)) {
  945. log.warn( "Namespace path for repository {} does not exist: {}", getId(), deleteTarget );
  946. throw new ContentNotFoundException( "Namespace not found for repository "+getId()+": "+path );
  947. }
  948. if ( Files.isDirectory(deleteTarget) )
  949. {
  950. try
  951. {
  952. org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
  953. }
  954. catch ( IOException e )
  955. {
  956. log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
  957. throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
  958. }
  959. } else {
  960. log.warn( "Namespace path for repository {} is not a directory {}", getId(), deleteTarget );
  961. throw new ContentNotFoundException( "Namespace path for repository "+getId()+" is not directory: " + path );
  962. }
  963. }
  964. @Override
  965. public String getId()
  966. {
  967. return repository.getId();
  968. }
  969. @Override
  970. public List<ArtifactReference> getRelatedArtifacts( VersionedReference reference )
  971. throws ContentNotFoundException, LayoutException, ContentAccessException
  972. {
  973. StorageAsset artifactDir = toFile( reference );
  974. if ( !artifactDir.exists())
  975. {
  976. throw new ContentNotFoundException(
  977. "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath() );
  978. }
  979. if ( !artifactDir.isContainer() )
  980. {
  981. throw new ContentNotFoundException(
  982. "Unable to get related artifacts using a non-directory: " + artifactDir.getPath() );
  983. }
  984. // First gather up the versions found as artifacts in the managed repository.
  985. try (Stream<? extends StorageAsset> stream = artifactDir.list().stream() ) {
  986. return stream.filter(asset -> !asset.isContainer()).map(path -> {
  987. try {
  988. ArtifactReference artifact = toArtifactReference(path.getPath());
  989. if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals(
  990. reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) {
  991. return artifact;
  992. } else {
  993. return null;
  994. }
  995. } catch (LayoutException e) {
  996. log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
  997. return null;
  998. }
  999. }).filter(Objects::nonNull).collect(Collectors.toList());
  1000. } catch (RuntimeException e) {
  1001. Throwable cause = e.getCause( );
  1002. if (cause!=null) {
  1003. if (cause instanceof LayoutException) {
  1004. throw (LayoutException)cause;
  1005. } else
  1006. {
  1007. throw new ContentAccessException( cause.getMessage( ), cause );
  1008. }
  1009. } else {
  1010. throw new ContentAccessException( e.getMessage( ), e );
  1011. }
  1012. }
  1013. }
  1014. /*
  1015. * Create the filter for various combinations of classifier and type
  1016. */
  1017. private Predicate<ArtifactReference> getChecker(ArtifactReference referenceObject, String extension) {
  1018. // TODO: Check, if extension is the correct parameter here
  1019. // We compare type with extension which works for artifacts like .jar.md5 but may
  1020. // be not the best way.
  1021. if (referenceObject.getClassifier()!=null && referenceObject.getType()!=null) {
  1022. return ((ArtifactReference a) ->
  1023. referenceObject.getGroupId().equals( a.getGroupId() )
  1024. && referenceObject.getArtifactId().equals( a.getArtifactId() )
  1025. && referenceObject.getVersion( ).equals( a.getVersion( ) )
  1026. && ( (a.getType()==null)
  1027. || referenceObject.getType().equals( a.getType() )
  1028. || a.getType().startsWith(extension) )
  1029. && referenceObject.getClassifier().equals( a.getClassifier() )
  1030. );
  1031. } else if (referenceObject.getClassifier()!=null && referenceObject.getType()==null){
  1032. return ((ArtifactReference a) ->
  1033. referenceObject.getGroupId().equals( a.getGroupId() )
  1034. && referenceObject.getArtifactId().equals( a.getArtifactId() )
  1035. && referenceObject.getVersion( ).equals( a.getVersion( ) )
  1036. && referenceObject.getClassifier().equals( a.getClassifier() )
  1037. );
  1038. } else if (referenceObject.getClassifier()==null && referenceObject.getType()!=null){
  1039. return ((ArtifactReference a) ->
  1040. referenceObject.getGroupId().equals( a.getGroupId() )
  1041. && referenceObject.getArtifactId().equals( a.getArtifactId() )
  1042. && referenceObject.getVersion( ).equals( a.getVersion( ) )
  1043. && ( (a.getType()==null)
  1044. || referenceObject.getType().equals( a.getType() )
  1045. || a.getType().startsWith(extension) )
  1046. );
  1047. } else {
  1048. return ((ArtifactReference a) ->
  1049. referenceObject.getGroupId().equals( a.getGroupId() )
  1050. && referenceObject.getArtifactId().equals( a.getArtifactId() )
  1051. && referenceObject.getVersion( ).equals( a.getVersion( ) )
  1052. );
  1053. }
  1054. }
  1055. @Override
  1056. public String getRepoRoot()
  1057. {
  1058. return convertUriToPath( repository.getLocation() );
  1059. }
  1060. private String convertUriToPath( URI uri ) {
  1061. if (uri.getScheme()==null) {
  1062. return Paths.get(uri.getPath()).toString();
  1063. } else if ("file".equals(uri.getScheme())) {
  1064. return Paths.get(uri).toString();
  1065. } else {
  1066. return uri.toString();
  1067. }
  1068. }
  1069. @Override
  1070. public ManagedRepository getRepository()
  1071. {
  1072. return repository;
  1073. }
  1074. @Override
  1075. public boolean hasContent( ArtifactReference reference ) throws ContentAccessException
  1076. {
  1077. StorageAsset artifactFile = toFile( reference );
  1078. return artifactFile.exists() && !artifactFile.isContainer();
  1079. }
  1080. @Override
  1081. public boolean hasContent( VersionedReference reference ) throws ContentAccessException
  1082. {
  1083. try
  1084. {
  1085. return ( getFirstArtifact( reference ) != null );
  1086. }
  1087. catch ( LayoutException | ContentNotFoundException e )
  1088. {
  1089. return false;
  1090. }
  1091. catch ( IOException e )
  1092. {
  1093. String path = toPath( reference );
  1094. log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
  1095. throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
  1096. }
  1097. }
  1098. @Override
  1099. public void setRepository( final ManagedRepository repo )
  1100. {
  1101. this.repository = repo;
  1102. if (repo!=null) {
  1103. if (repository instanceof EditableManagedRepository) {
  1104. ((EditableManagedRepository) repository).setContent(this);
  1105. }
  1106. }
  1107. }
  1108. private Path getRepoDir() {
  1109. return repository.getAsset( "" ).getFilePath( );
  1110. }
  1111. private RepositoryStorage getStorage() {
  1112. return repository.getAsset( "" ).getStorage( );
  1113. }
  1114. /**
  1115. * Convert a path to an artifact reference.
  1116. *
  1117. * @param path the path to convert. (relative or full location path)
  1118. * @throws LayoutException if the path cannot be converted to an artifact reference.
  1119. */
  1120. @Override
  1121. public ArtifactReference toArtifactReference( String path )
  1122. throws LayoutException
  1123. {
  1124. String repoPath = convertUriToPath( repository.getLocation() );
  1125. if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 )
  1126. {
  1127. return super.toArtifactReference( path.substring( repoPath.length() + 1 ) );
  1128. } else {
  1129. repoPath = path;
  1130. if (repoPath!=null) {
  1131. while (repoPath.startsWith("/")) {
  1132. repoPath = repoPath.substring(1);
  1133. }
  1134. }
  1135. return super.toArtifactReference( repoPath );
  1136. }
  1137. }
  1138. // The variant with runtime exception for stream usage
  1139. private ArtifactReference toArtifactRef(String path) {
  1140. try {
  1141. return toArtifactReference(path);
  1142. } catch (LayoutException e) {
  1143. throw new RuntimeException(e);
  1144. }
  1145. }
  1146. @Override
  1147. public StorageAsset toFile( ArtifactReference reference )
  1148. {
  1149. return repository.getAsset(toPath(reference));
  1150. }
  1151. @Override
  1152. public StorageAsset toFile( ArchivaArtifact reference )
  1153. {
  1154. return repository.getAsset( toPath( reference ) );
  1155. }
  1156. @Override
  1157. public StorageAsset toFile( VersionedReference reference )
  1158. {
  1159. return repository.getAsset( toPath( reference ) );
  1160. }
  1161. /**
  1162. * Get the first Artifact found in the provided VersionedReference location.
  1163. *
  1164. * @param reference the reference to the versioned reference to search within
  1165. * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
  1166. * no artifact was found within the versioned reference.
  1167. * @throws java.io.IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
  1168. * @throws LayoutException
  1169. */
  1170. private ArtifactReference getFirstArtifact( VersionedReference reference )
  1171. throws ContentNotFoundException, LayoutException, IOException
  1172. {
  1173. try(Stream<ArtifactReference> stream = newArtifactStream( reference ))
  1174. {
  1175. return stream.findFirst( ).orElse( null );
  1176. } catch (RuntimeException e) {
  1177. throw new ContentNotFoundException( e.getMessage( ), e.getCause( ) );
  1178. }
  1179. }
  1180. private Stream<ArtifactReference> newArtifactStream( VersionedReference reference) throws ContentNotFoundException, LayoutException, IOException {
  1181. final Path repoBase = getRepoDir( );
  1182. String path = toMetadataPath( reference );
  1183. Path versionDir = repoBase.resolve( path ).getParent();
  1184. if ( !Files.exists(versionDir) )
  1185. {
  1186. throw new ContentNotFoundException( "Unable to gather the list of artifacts on a non-existant directory: "
  1187. + versionDir.toAbsolutePath() );
  1188. }
  1189. if ( !Files.isDirectory(versionDir) )
  1190. {
  1191. throw new ContentNotFoundException(
  1192. "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath() );
  1193. }
  1194. return Files.list(versionDir).filter(Files::isRegularFile)
  1195. .map(p -> repoBase.relativize(p).toString())
  1196. .filter(p -> !filetypes.matchesDefaultExclusions(p))
  1197. .filter(filetypes::matchesArtifactPattern)
  1198. .map(this::toArtifactRef);
  1199. }
  1200. public List<ArtifactReference> getArtifacts(VersionedReference reference) throws ContentNotFoundException, LayoutException, ContentAccessException
  1201. {
  1202. try (Stream<ArtifactReference> stream = newArtifactStream( reference ))
  1203. {
  1204. return stream.collect( Collectors.toList( ) );
  1205. } catch ( IOException e )
  1206. {
  1207. String path = toPath( reference );
  1208. log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
  1209. throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
  1210. }
  1211. }
  1212. private boolean hasArtifact( VersionedReference reference )
  1213. {
  1214. try(Stream<ArtifactReference> stream = newArtifactStream( reference ))
  1215. {
  1216. return stream.anyMatch( e -> true );
  1217. } catch (ContentNotFoundException e) {
  1218. return false;
  1219. } catch ( LayoutException | IOException e) {
  1220. // We throw the runtime exception for better stream handling
  1221. throw new RuntimeException(e);
  1222. }
  1223. }
  1224. public void setFiletypes( FileTypes filetypes )
  1225. {
  1226. this.filetypes = filetypes;
  1227. }
  1228. public void setMavenContentHelper( MavenContentHelper contentHelper) {
  1229. this.mavenContentHelper = contentHelper;
  1230. }
  1231. }