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.

Maven2RepositoryPathTranslator.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. package org.apache.archiva.metadata.repository.storage.maven2;
  2. /*
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. */
  20. import org.apache.archiva.common.utils.VersionUtil;
  21. import org.apache.archiva.metadata.model.ArtifactMetadata;
  22. import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
  23. import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
  24. import org.apache.archiva.repository.storage.StorageAsset;
  25. import org.slf4j.Logger;
  26. import org.slf4j.LoggerFactory;
  27. import org.springframework.stereotype.Service;
  28. import javax.annotation.PostConstruct;
  29. import javax.inject.Inject;
  30. import java.nio.file.Path;
  31. import java.util.List;
  32. import java.util.regex.Matcher;
  33. import java.util.regex.Pattern;
  34. /**
  35. *
  36. */
  37. @Service( "repositoryPathTranslator#maven2" )
  38. public class Maven2RepositoryPathTranslator
  39. implements RepositoryPathTranslator
  40. {
  41. private Logger log = LoggerFactory.getLogger( getClass() );
  42. private static final char GROUP_SEPARATOR = '.';
  43. private static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "([0-9]{8}.[0-9]{6})-([0-9]+).*" );
  44. private static final Pattern MAVEN_PLUGIN_PATTERN = Pattern.compile( "^(maven-.*-plugin)|(.*-maven-plugin)$" );
  45. /**
  46. *
  47. * see #initialize
  48. */
  49. @Inject
  50. private List<ArtifactMappingProvider> artifactMappingProviders;
  51. public Maven2RepositoryPathTranslator()
  52. {
  53. // noop
  54. }
  55. @PostConstruct
  56. public void initialize()
  57. {
  58. //artifactMappingProviders = new ArrayList<ArtifactMappingProvider>(
  59. // applicationContext.getBeansOfType( ArtifactMappingProvider.class ).values() );
  60. }
  61. public Maven2RepositoryPathTranslator( List<ArtifactMappingProvider> artifactMappingProviders )
  62. {
  63. this.artifactMappingProviders = artifactMappingProviders;
  64. }
  65. @Override
  66. public StorageAsset toFile(StorageAsset basedir, String namespace, String projectId, String projectVersion, String filename )
  67. {
  68. return basedir.resolve( toPath( namespace, projectId, projectVersion, filename ) );
  69. }
  70. @Override
  71. public StorageAsset toFile( StorageAsset basedir, String namespace, String projectId, String projectVersion )
  72. {
  73. return basedir.resolve( toPath( namespace, projectId, projectVersion ) );
  74. }
  75. @Override
  76. public String toPath( String namespace, String projectId, String projectVersion, String filename )
  77. {
  78. StringBuilder path = new StringBuilder();
  79. appendNamespaceToProjectVersion( path, namespace, projectId, projectVersion );
  80. path.append( PATH_SEPARATOR );
  81. path.append( filename );
  82. return path.toString();
  83. }
  84. private void appendNamespaceToProjectVersion( StringBuilder path, String namespace, String projectId,
  85. String projectVersion )
  86. {
  87. appendNamespaceAndProject( path, namespace, projectId );
  88. path.append( projectVersion );
  89. }
  90. public String toPath( String namespace, String projectId, String projectVersion )
  91. {
  92. StringBuilder path = new StringBuilder();
  93. appendNamespaceToProjectVersion( path, namespace, projectId, projectVersion );
  94. return path.toString();
  95. }
  96. public String toPath( String namespace )
  97. {
  98. StringBuilder path = new StringBuilder();
  99. appendNamespace( path, namespace );
  100. return path.toString();
  101. }
  102. @Override
  103. public String toPath( String namespace, String projectId )
  104. {
  105. StringBuilder path = new StringBuilder();
  106. appendNamespaceAndProject( path, namespace, projectId );
  107. return path.toString();
  108. }
  109. private void appendNamespaceAndProject( StringBuilder path, String namespace, String projectId )
  110. {
  111. appendNamespace( path, namespace );
  112. path.append( projectId ).append( PATH_SEPARATOR );
  113. }
  114. private void appendNamespace( StringBuilder path, String namespace )
  115. {
  116. path.append( formatAsDirectory( namespace ) ).append( PATH_SEPARATOR );
  117. }
  118. @Override
  119. public StorageAsset toFile( StorageAsset basedir, String namespace, String projectId )
  120. {
  121. return basedir.resolve( toPath( namespace, projectId ) );
  122. }
  123. @Override
  124. public StorageAsset toFile( StorageAsset basedir, String namespace )
  125. {
  126. return basedir.resolve( toPath( namespace ) );
  127. }
  128. private String formatAsDirectory( String directory )
  129. {
  130. return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
  131. }
  132. @Override
  133. public ArtifactMetadata getArtifactForPath( String repoId, String relativePath )
  134. {
  135. String[] parts = relativePath.replace( '\\', '/' ).split( "/" );
  136. int len = parts.length;
  137. if ( len < 4 )
  138. {
  139. throw new IllegalArgumentException(
  140. "Not a valid artifact path in a Maven 2 repository, not enough directories: " + relativePath );
  141. }
  142. String id = parts[--len];
  143. String baseVersion = parts[--len];
  144. String artifactId = parts[--len];
  145. StringBuilder groupIdBuilder = new StringBuilder();
  146. for ( int i = 0; i < len - 1; i++ )
  147. {
  148. groupIdBuilder.append( parts[i] );
  149. groupIdBuilder.append( '.' );
  150. }
  151. groupIdBuilder.append( parts[len - 1] );
  152. return getArtifactFromId( repoId, groupIdBuilder.toString(), artifactId, baseVersion, id );
  153. }
  154. @Override
  155. public ArtifactMetadata getArtifactFromId( String repoId, String namespace, String projectId, String projectVersion,
  156. String id )
  157. {
  158. if ( !id.startsWith( projectId + "-" ) )
  159. {
  160. throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
  161. + "' doesn't start with artifact ID '" + projectId + "'" );
  162. }
  163. MavenArtifactFacet facet = new MavenArtifactFacet();
  164. int index = projectId.length() + 1;
  165. String version;
  166. String idSubStrFromVersion = id.substring( index );
  167. if ( idSubStrFromVersion.startsWith( projectVersion ) && !VersionUtil.isUniqueSnapshot( projectVersion ) )
  168. {
  169. // non-snapshot versions, or non-timestamped snapshot versions
  170. version = projectVersion;
  171. }
  172. else if ( VersionUtil.isGenericSnapshot( projectVersion ) )
  173. {
  174. // timestamped snapshots
  175. try
  176. {
  177. int mainVersionLength = projectVersion.length() - 8; // 8 is length of "SNAPSHOT"
  178. if ( mainVersionLength == 0 )
  179. {
  180. throw new IllegalArgumentException(
  181. "Timestamped snapshots must contain the main version, filename was '" + id + "'" );
  182. }
  183. Matcher m = TIMESTAMP_PATTERN.matcher( idSubStrFromVersion.substring( mainVersionLength ) );
  184. m.matches();
  185. String timestamp = m.group( 1 );
  186. String buildNumber = m.group( 2 );
  187. facet.setTimestamp( timestamp );
  188. facet.setBuildNumber( Integer.parseInt( buildNumber ) );
  189. version = idSubStrFromVersion.substring( 0, mainVersionLength ) + timestamp + "-" + buildNumber;
  190. }
  191. catch ( IllegalStateException e )
  192. {
  193. throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
  194. + "' doesn't contain a timestamped version matching snapshot '"
  195. + projectVersion + "'", e);
  196. }
  197. }
  198. else
  199. {
  200. // invalid
  201. throw new IllegalArgumentException(
  202. "Not a valid artifact path in a Maven 2 repository, filename '" + id + "' doesn't contain version '"
  203. + projectVersion + "'" );
  204. }
  205. String classifier;
  206. String ext;
  207. index += version.length();
  208. if ( index == id.length() )
  209. {
  210. // no classifier or extension
  211. classifier = null;
  212. ext = null;
  213. }
  214. else
  215. {
  216. char c = id.charAt( index );
  217. if ( c == '-' )
  218. {
  219. // classifier up until '.'
  220. int extIndex = id.indexOf( '.', index );
  221. if ( extIndex >= 0 )
  222. {
  223. classifier = id.substring( index + 1, extIndex );
  224. ext = id.substring( extIndex + 1 );
  225. }
  226. else
  227. {
  228. classifier = id.substring( index + 1 );
  229. ext = null;
  230. }
  231. }
  232. else if ( c == '.' )
  233. {
  234. // rest is the extension
  235. classifier = null;
  236. ext = id.substring( index + 1 );
  237. }
  238. else
  239. {
  240. throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
  241. + "' expected classifier or extension but got '"
  242. + id.substring( index ) + "'" );
  243. }
  244. }
  245. ArtifactMetadata metadata = new ArtifactMetadata();
  246. metadata.setId( id );
  247. metadata.setNamespace( namespace );
  248. metadata.setProject( projectId );
  249. metadata.setRepositoryId( repoId );
  250. metadata.setProjectVersion( projectVersion );
  251. metadata.setVersion( version );
  252. facet.setClassifier( classifier );
  253. // we use our own provider here instead of directly accessing Maven's artifact handlers as it has no way
  254. // to select the correct order to apply multiple extensions mappings to a preferred type
  255. // TODO: this won't allow the user to decide order to apply them if there are conflicts or desired changes -
  256. // perhaps the plugins could register missing entries in configuration, then we just use configuration
  257. // here?
  258. String type = null;
  259. for ( ArtifactMappingProvider mapping : artifactMappingProviders )
  260. {
  261. type = mapping.mapClassifierAndExtensionToType( classifier, ext );
  262. if ( type != null )
  263. {
  264. break;
  265. }
  266. }
  267. // TODO: this is cheating! We should check the POM metadata instead
  268. if ( type == null && "jar".equals( ext ) && isArtifactIdValidMavenPlugin( projectId ) )
  269. {
  270. type = "maven-plugin";
  271. }
  272. // use extension as default
  273. if ( type == null )
  274. {
  275. type = ext;
  276. }
  277. // TODO: should we allow this instead?
  278. if ( type == null )
  279. {
  280. throw new IllegalArgumentException(
  281. "Not a valid artifact path in a Maven 2 repository, filename '" + id + "' does not have a type" );
  282. }
  283. facet.setType( type );
  284. metadata.addFacet( facet );
  285. return metadata;
  286. }
  287. public boolean isArtifactIdValidMavenPlugin( String artifactId )
  288. {
  289. return MAVEN_PLUGIN_PATTERN.matcher( artifactId ).matches();
  290. }
  291. }