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 61KB

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