001 package org.apache.archiva.metadata.repository.file; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import org.apache.archiva.configuration.ArchivaConfiguration; 023 import org.apache.archiva.configuration.ManagedRepositoryConfiguration; 024 import org.apache.archiva.metadata.model.ArtifactMetadata; 025 import org.apache.archiva.metadata.model.CiManagement; 026 import org.apache.archiva.metadata.model.Dependency; 027 import org.apache.archiva.metadata.model.IssueManagement; 028 import org.apache.archiva.metadata.model.License; 029 import org.apache.archiva.metadata.model.MailingList; 030 import org.apache.archiva.metadata.model.MetadataFacet; 031 import org.apache.archiva.metadata.model.MetadataFacetFactory; 032 import org.apache.archiva.metadata.model.Organization; 033 import org.apache.archiva.metadata.model.ProjectMetadata; 034 import org.apache.archiva.metadata.model.ProjectVersionMetadata; 035 import org.apache.archiva.metadata.model.ProjectVersionReference; 036 import org.apache.archiva.metadata.model.Scm; 037 import org.apache.archiva.metadata.repository.MetadataRepository; 038 import org.apache.archiva.metadata.repository.MetadataRepositoryException; 039 import org.apache.commons.io.FileUtils; 040 import org.apache.commons.io.IOUtils; 041 import org.slf4j.Logger; 042 import org.slf4j.LoggerFactory; 043 044 import java.io.File; 045 import java.io.FileInputStream; 046 import java.io.FileNotFoundException; 047 import java.io.FileOutputStream; 048 import java.io.IOException; 049 import java.util.ArrayList; 050 import java.util.Arrays; 051 import java.util.Collection; 052 import java.util.Collections; 053 import java.util.Comparator; 054 import java.util.Date; 055 import java.util.HashMap; 056 import java.util.HashSet; 057 import java.util.LinkedHashSet; 058 import java.util.List; 059 import java.util.Map; 060 import java.util.Properties; 061 import java.util.Set; 062 import java.util.StringTokenizer; 063 064 public class FileMetadataRepository 065 implements MetadataRepository 066 { 067 private final Map<String, MetadataFacetFactory> metadataFacetFactories; 068 069 private final ArchivaConfiguration configuration; 070 071 private Logger log = LoggerFactory.getLogger( FileMetadataRepository.class ); 072 073 private static final String PROJECT_METADATA_KEY = "project-metadata"; 074 075 private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata"; 076 077 private static final String NAMESPACE_METADATA_KEY = "namespace-metadata"; 078 079 private static final String METADATA_KEY = "metadata"; 080 081 public FileMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories, 082 ArchivaConfiguration configuration ) 083 { 084 this.metadataFacetFactories = metadataFacetFactories; 085 this.configuration = configuration; 086 } 087 088 private File getBaseDirectory( String repoId ) 089 { 090 // TODO: should be configurable, like the index 091 String basedir = configuration.getConfiguration().getManagedRepositoriesAsMap().get( repoId ).getLocation(); 092 return new File( basedir, ".archiva" ); 093 } 094 095 private File getDirectory( String repoId ) 096 { 097 return new File( getBaseDirectory( repoId ), "content" ); 098 } 099 100 public void updateProject( String repoId, ProjectMetadata project ) 101 { 102 updateProject( repoId, project.getNamespace(), project.getId() ); 103 } 104 105 private void updateProject( String repoId, String namespace, String id ) 106 { 107 // TODO: this is a more braindead implementation than we would normally expect, for prototyping purposes 108 updateNamespace( repoId, namespace ); 109 110 try 111 { 112 File namespaceDirectory = new File( getDirectory( repoId ), namespace ); 113 Properties properties = new Properties(); 114 properties.setProperty( "namespace", namespace ); 115 properties.setProperty( "id", id ); 116 writeProperties( properties, new File( namespaceDirectory, id ), PROJECT_METADATA_KEY ); 117 } 118 catch ( IOException e ) 119 { 120 // TODO! 121 log.error( e.getMessage(), e ); 122 } 123 } 124 125 public void updateProjectVersion( String repoId, String namespace, String projectId, 126 ProjectVersionMetadata versionMetadata ) 127 { 128 updateProject( repoId, namespace, projectId ); 129 130 File directory = 131 new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + versionMetadata.getId() ); 132 133 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 134 // remove properties that are not references or artifacts 135 for ( Object key : new ArrayList<Object>( properties.keySet() ) ) 136 { 137 String name = (String) key; 138 if ( !name.contains( ":" ) && !name.equals( "facetIds" ) ) 139 { 140 properties.remove( name ); 141 } 142 143 // clear the facet contents so old properties are no longer written 144 clearMetadataFacetProperties( versionMetadata.getFacetList(), properties, "" ); 145 } 146 properties.setProperty( "id", versionMetadata.getId() ); 147 setProperty( properties, "name", versionMetadata.getName() ); 148 setProperty( properties, "description", versionMetadata.getDescription() ); 149 setProperty( properties, "url", versionMetadata.getUrl() ); 150 setProperty( properties, "incomplete", String.valueOf( versionMetadata.isIncomplete() ) ); 151 if ( versionMetadata.getScm() != null ) 152 { 153 setProperty( properties, "scm.connection", versionMetadata.getScm().getConnection() ); 154 setProperty( properties, "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() ); 155 setProperty( properties, "scm.url", versionMetadata.getScm().getUrl() ); 156 } 157 if ( versionMetadata.getCiManagement() != null ) 158 { 159 setProperty( properties, "ci.system", versionMetadata.getCiManagement().getSystem() ); 160 setProperty( properties, "ci.url", versionMetadata.getCiManagement().getUrl() ); 161 } 162 if ( versionMetadata.getIssueManagement() != null ) 163 { 164 setProperty( properties, "issue.system", versionMetadata.getIssueManagement().getSystem() ); 165 setProperty( properties, "issue.url", versionMetadata.getIssueManagement().getUrl() ); 166 } 167 if ( versionMetadata.getOrganization() != null ) 168 { 169 setProperty( properties, "org.name", versionMetadata.getOrganization().getName() ); 170 setProperty( properties, "org.url", versionMetadata.getOrganization().getUrl() ); 171 } 172 int i = 0; 173 for ( License license : versionMetadata.getLicenses() ) 174 { 175 setProperty( properties, "license." + i + ".name", license.getName() ); 176 setProperty( properties, "license." + i + ".url", license.getUrl() ); 177 i++; 178 } 179 i = 0; 180 for ( MailingList mailingList : versionMetadata.getMailingLists() ) 181 { 182 setProperty( properties, "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() ); 183 setProperty( properties, "mailingList." + i + ".name", mailingList.getName() ); 184 setProperty( properties, "mailingList." + i + ".post", mailingList.getPostAddress() ); 185 setProperty( properties, "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() ); 186 setProperty( properties, "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() ); 187 setProperty( properties, "mailingList." + i + ".otherArchives", join( mailingList.getOtherArchives() ) ); 188 i++; 189 } 190 i = 0; 191 ProjectVersionReference reference = new ProjectVersionReference(); 192 reference.setNamespace( namespace ); 193 reference.setProjectId( projectId ); 194 reference.setProjectVersion( versionMetadata.getId() ); 195 reference.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY ); 196 for ( Dependency dependency : versionMetadata.getDependencies() ) 197 { 198 setProperty( properties, "dependency." + i + ".classifier", dependency.getClassifier() ); 199 setProperty( properties, "dependency." + i + ".scope", dependency.getScope() ); 200 setProperty( properties, "dependency." + i + ".systemPath", dependency.getSystemPath() ); 201 setProperty( properties, "dependency." + i + ".artifactId", dependency.getArtifactId() ); 202 setProperty( properties, "dependency." + i + ".groupId", dependency.getGroupId() ); 203 setProperty( properties, "dependency." + i + ".version", dependency.getVersion() ); 204 setProperty( properties, "dependency." + i + ".type", dependency.getType() ); 205 setProperty( properties, "dependency." + i + ".optional", String.valueOf( dependency.isOptional() ) ); 206 207 updateProjectReference( repoId, dependency.getGroupId(), dependency.getArtifactId(), 208 dependency.getVersion(), reference ); 209 210 i++; 211 } 212 Set<String> facetIds = new LinkedHashSet<String>( versionMetadata.getFacetIds() ); 213 facetIds.addAll( Arrays.asList( properties.getProperty( "facetIds", "" ).split( "," ) ) ); 214 properties.setProperty( "facetIds", join( facetIds ) ); 215 216 updateProjectVersionFacets( versionMetadata, properties ); 217 218 try 219 { 220 writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); 221 } 222 catch ( IOException e ) 223 { 224 // TODO 225 log.error( e.getMessage(), e ); 226 } 227 } 228 229 private void updateProjectVersionFacets( ProjectVersionMetadata versionMetadata, Properties properties ) 230 { 231 for ( MetadataFacet facet : versionMetadata.getFacetList() ) 232 { 233 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() ) 234 { 235 properties.setProperty( facet.getFacetId() + ":" + entry.getKey(), entry.getValue() ); 236 } 237 } 238 } 239 240 private static void clearMetadataFacetProperties( Collection<MetadataFacet> facetList, Properties properties, 241 String prefix ) 242 { 243 List<Object> propsToRemove = new ArrayList<Object>(); 244 for ( MetadataFacet facet : facetList ) 245 { 246 for ( Object key : properties.keySet() ) 247 { 248 String keyString = (String) key; 249 if ( keyString.startsWith( prefix + facet.getFacetId() + ":" ) ) 250 { 251 propsToRemove.add( key ); 252 } 253 } 254 } 255 256 for ( Object key : propsToRemove ) 257 { 258 properties.remove( key ); 259 } 260 } 261 262 private void updateProjectReference( String repoId, String namespace, String projectId, String projectVersion, 263 ProjectVersionReference reference ) 264 { 265 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion ); 266 267 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 268 int i = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1; 269 setProperty( properties, "ref:lastReferenceNum", Integer.toString( i ) ); 270 setProperty( properties, "ref:reference." + i + ".namespace", reference.getNamespace() ); 271 setProperty( properties, "ref:reference." + i + ".projectId", reference.getProjectId() ); 272 setProperty( properties, "ref:reference." + i + ".projectVersion", reference.getProjectVersion() ); 273 setProperty( properties, "ref:reference." + i + ".referenceType", reference.getReferenceType().toString() ); 274 275 try 276 { 277 writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); 278 } 279 catch ( IOException e ) 280 { 281 // TODO 282 log.error( e.getMessage(), e ); 283 } 284 } 285 286 public void updateNamespace( String repoId, String namespace ) 287 { 288 try 289 { 290 File namespaceDirectory = new File( getDirectory( repoId ), namespace ); 291 Properties properties = new Properties(); 292 properties.setProperty( "namespace", namespace ); 293 writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY ); 294 295 } 296 catch ( IOException e ) 297 { 298 // TODO! 299 log.error( e.getMessage(), e ); 300 } 301 } 302 303 public List<String> getMetadataFacets( String repoId, String facetId ) 304 { 305 File directory = getMetadataDirectory( repoId, facetId ); 306 List<String> facets = new ArrayList<String>(); 307 recurse( facets, "", directory ); 308 return facets; 309 } 310 311 public boolean hasMetadataFacet( String repositoryId, String facetId ) 312 throws MetadataRepositoryException 313 { 314 // TODO could be improved a bit 315 return !getMetadataFacets( repositoryId, facetId ).isEmpty(); 316 } 317 318 private void recurse( List<String> facets, String prefix, File directory ) 319 { 320 File[] list = directory.listFiles(); 321 if ( list != null ) 322 { 323 for ( File dir : list ) 324 { 325 if ( dir.isDirectory() ) 326 { 327 recurse( facets, prefix + "/" + dir.getName(), dir ); 328 } 329 else if ( dir.getName().equals( METADATA_KEY + ".properties" ) ) 330 { 331 facets.add( prefix.substring( 1 ) ); 332 } 333 } 334 } 335 } 336 337 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name ) 338 { 339 Properties properties; 340 try 341 { 342 properties = 343 readProperties( new File( getMetadataDirectory( repositoryId, facetId ), name ), METADATA_KEY ); 344 } 345 catch ( FileNotFoundException e ) 346 { 347 return null; 348 } 349 catch ( IOException e ) 350 { 351 // TODO 352 log.error( e.getMessage(), e ); 353 return null; 354 } 355 MetadataFacet metadataFacet = null; 356 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId ); 357 if ( metadataFacetFactory != null ) 358 { 359 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name ); 360 Map<String, String> map = new HashMap<String, String>(); 361 for ( Object key : new ArrayList( properties.keySet() ) ) 362 { 363 String property = (String) key; 364 map.put( property, properties.getProperty( property ) ); 365 } 366 metadataFacet.fromProperties( map ); 367 } 368 return metadataFacet; 369 } 370 371 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet ) 372 { 373 Properties properties = new Properties(); 374 properties.putAll( metadataFacet.toProperties() ); 375 376 try 377 { 378 File directory = 379 new File( getMetadataDirectory( repositoryId, metadataFacet.getFacetId() ), metadataFacet.getName() ); 380 writeProperties( properties, directory, METADATA_KEY ); 381 } 382 catch ( IOException e ) 383 { 384 // TODO! 385 log.error( e.getMessage(), e ); 386 } 387 } 388 389 public void removeMetadataFacets( String repositoryId, String facetId ) 390 { 391 File dir = getMetadataDirectory( repositoryId, facetId ); 392 if ( !FileUtils.deleteQuietly( dir ) ) 393 { 394 log.error( "Cannot delete the metadata repository {}", dir ); 395 } 396 } 397 398 public void removeMetadataFacet( String repoId, String facetId, String name ) 399 { 400 File dir = new File( getMetadataDirectory( repoId, facetId ), name ); 401 if ( !FileUtils.deleteQuietly( dir ) ) 402 { 403 log.error( "Cannot delete the metadata repository {}", dir ); 404 } 405 } 406 407 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime ) 408 { 409 // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index 410 // of this information (eg. in Lucene, as before) 411 412 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>(); 413 for ( String ns : getRootNamespaces( repoId ) ) 414 { 415 getArtifactsByDateRange( artifacts, repoId, ns, startTime, endTime ); 416 } 417 Collections.sort( artifacts, new ArtifactComparator() ); 418 return artifacts; 419 } 420 421 private void getArtifactsByDateRange( List<ArtifactMetadata> artifacts, String repoId, String ns, Date startTime, 422 Date endTime ) 423 { 424 for ( String namespace : getNamespaces( repoId, ns ) ) 425 { 426 getArtifactsByDateRange( artifacts, repoId, ns + "." + namespace, startTime, endTime ); 427 } 428 429 for ( String project : getProjects( repoId, ns ) ) 430 { 431 for ( String version : getProjectVersions( repoId, ns, project ) ) 432 { 433 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) ) 434 { 435 if ( startTime == null || startTime.before( artifact.getWhenGathered() ) ) 436 { 437 if ( endTime == null || endTime.after( artifact.getWhenGathered() ) ) 438 { 439 artifacts.add( artifact ); 440 } 441 } 442 } 443 } 444 } 445 } 446 447 public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId, 448 String projectVersion ) 449 { 450 Map<String, ArtifactMetadata> artifacts = new HashMap<String, ArtifactMetadata>(); 451 452 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion ); 453 454 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 455 456 for ( Map.Entry entry : properties.entrySet() ) 457 { 458 String name = (String) entry.getKey(); 459 StringTokenizer tok = new StringTokenizer( name, ":" ); 460 if ( tok.hasMoreTokens() && "artifact".equals( tok.nextToken() ) ) 461 { 462 String field = tok.nextToken(); 463 String id = tok.nextToken(); 464 465 ArtifactMetadata artifact = artifacts.get( id ); 466 if ( artifact == null ) 467 { 468 artifact = new ArtifactMetadata(); 469 artifact.setRepositoryId( repoId ); 470 artifact.setNamespace( namespace ); 471 artifact.setProject( projectId ); 472 artifact.setProjectVersion( projectVersion ); 473 artifact.setVersion( projectVersion ); 474 artifact.setId( id ); 475 artifacts.put( id, artifact ); 476 } 477 478 String value = (String) entry.getValue(); 479 if ( "updated".equals( field ) ) 480 { 481 artifact.setFileLastModified( Long.parseLong( value ) ); 482 } 483 else if ( "size".equals( field ) ) 484 { 485 artifact.setSize( Long.valueOf( value ) ); 486 } 487 else if ( "whenGathered".equals( field ) ) 488 { 489 artifact.setWhenGathered( new Date( Long.parseLong( value ) ) ); 490 } 491 else if ( "version".equals( field ) ) 492 { 493 artifact.setVersion( value ); 494 } 495 else if ( "md5".equals( field ) ) 496 { 497 artifact.setMd5( value ); 498 } 499 else if ( "sha1".equals( field ) ) 500 { 501 artifact.setSha1( value ); 502 } 503 else if ( "facetIds".equals( field ) ) 504 { 505 if ( value.length() > 0 ) 506 { 507 String propertyPrefix = "artifact:facet:" + id + ":"; 508 for ( String facetId : value.split( "," ) ) 509 { 510 MetadataFacetFactory factory = metadataFacetFactories.get( facetId ); 511 if ( factory == null ) 512 { 513 log.error( "Attempted to load unknown artifact metadata facet: " + facetId ); 514 } 515 else 516 { 517 MetadataFacet facet = factory.createMetadataFacet(); 518 String prefix = propertyPrefix + facet.getFacetId(); 519 Map<String, String> map = new HashMap<String, String>(); 520 for ( Object key : new ArrayList( properties.keySet() ) ) 521 { 522 String property = (String) key; 523 if ( property.startsWith( prefix ) ) 524 { 525 map.put( property.substring( prefix.length() + 1 ), 526 properties.getProperty( property ) ); 527 } 528 } 529 facet.fromProperties( map ); 530 artifact.addFacet( facet ); 531 } 532 } 533 } 534 535 updateArtifactFacets( artifact, properties ); 536 } 537 } 538 } 539 return artifacts.values(); 540 } 541 542 public void save() 543 { 544 // it's all instantly persisted 545 } 546 547 public void close() 548 { 549 // nothing additional to close 550 } 551 552 public void revert() 553 { 554 log.warn( "Attempted to revert a session, but the file-based repository storage doesn't support it" ); 555 } 556 557 public boolean canObtainAccess( Class<?> aClass ) 558 { 559 return false; 560 } 561 562 public <T>T obtainAccess( Class<T> aClass ) 563 { 564 throw new IllegalArgumentException( 565 "Access using " + aClass + " is not supported on the file metadata storage" ); 566 } 567 568 private void updateArtifactFacets( ArtifactMetadata artifact, Properties properties ) 569 { 570 String propertyPrefix = "artifact:facet:" + artifact.getId() + ":"; 571 for ( MetadataFacet facet : artifact.getFacetList() ) 572 { 573 for ( Map.Entry<String, String> e : facet.toProperties().entrySet() ) 574 { 575 String key = propertyPrefix + facet.getFacetId() + ":" + e.getKey(); 576 properties.setProperty( key, e.getValue() ); 577 } 578 } 579 } 580 581 public Collection<String> getRepositories() 582 { 583 List<String> repositories = new ArrayList<String>(); 584 for ( ManagedRepositoryConfiguration managedRepositoryConfiguration : configuration.getConfiguration().getManagedRepositories() ) 585 { 586 repositories.add( managedRepositoryConfiguration.getId() ); 587 } 588 return repositories; 589 } 590 591 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum ) 592 { 593 // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index 594 // of this information (eg. in Lucene, as before) 595 // alternatively, we could build a referential tree in the content repository, however it would need some levels 596 // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567) 597 598 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>(); 599 for ( String ns : getRootNamespaces( repositoryId ) ) 600 { 601 getArtifactsByChecksum( artifacts, repositoryId, ns, checksum ); 602 } 603 return artifacts; 604 } 605 606 public void removeNamespace( String repositoryId, String project ) 607 throws MetadataRepositoryException 608 { 609 try 610 { 611 File namespaceDirectory = new File( getDirectory( repositoryId ), project ); 612 FileUtils.deleteDirectory( namespaceDirectory ); 613 //Properties properties = new Properties(); 614 //properties.setProperty( "namespace", namespace ); 615 //writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY ); 616 617 } 618 catch ( IOException e ) 619 { 620 throw new MetadataRepositoryException( e.getMessage(), e ); 621 } 622 } 623 624 public void removeArtifact( ArtifactMetadata artifactMetadata, String baseVersion ) 625 throws MetadataRepositoryException 626 { 627 628 File directory = new File( getDirectory( artifactMetadata.getRepositoryId() ), 629 artifactMetadata.getNamespace() + "/" + artifactMetadata.getProject() + "/" 630 + baseVersion ); 631 632 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 633 634 String id = artifactMetadata.getId(); 635 636 properties.remove( "artifact:updated:" + id ); 637 properties.remove( "artifact:whenGathered:" + id ); 638 properties.remove( "artifact:size:" + id ); 639 properties.remove( "artifact:md5:" + id ); 640 properties.remove( "artifact:sha1:" + id ); 641 properties.remove( "artifact:version:" + id ); 642 properties.remove( "artifact:facetIds:" + id ); 643 644 String prefix = "artifact:facet:" + id + ":"; 645 for ( Object key : new ArrayList<Object>( properties.keySet() ) ) 646 { 647 String property = (String) key; 648 if ( property.startsWith( prefix ) ) 649 { 650 properties.remove( property ); 651 } 652 } 653 654 try 655 { 656 writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); 657 } 658 catch ( IOException e ) 659 { 660 // TODO 661 log.error( e.getMessage(), e ); 662 } 663 664 } 665 666 public void removeArtifact( String repoId, String namespace, String project, String version, String id ) 667 { 668 669 File directory = new File( getDirectory( repoId ), namespace + "/" + project + "/" + version ); 670 671 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 672 673 properties.remove( "artifact:updated:" + id ); 674 properties.remove( "artifact:whenGathered:" + id ); 675 properties.remove( "artifact:size:" + id ); 676 properties.remove( "artifact:md5:" + id ); 677 properties.remove( "artifact:sha1:" + id ); 678 properties.remove( "artifact:version:" + id ); 679 properties.remove( "artifact:facetIds:" + id ); 680 681 String prefix = "artifact:facet:" + id + ":"; 682 for ( Object key : new ArrayList<Object>( properties.keySet() ) ) 683 { 684 String property = (String) key; 685 if ( property.startsWith( prefix ) ) 686 { 687 properties.remove( property ); 688 } 689 } 690 691 try 692 { 693 694 FileUtils.deleteDirectory( directory ); 695 696 //writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); 697 } 698 catch ( IOException e ) 699 { 700 // TODO 701 log.error( e.getMessage(), e ); 702 } 703 } 704 705 /** 706 * FIXME implements this !!!! 707 * 708 * @param repositoryId 709 * @param namespace 710 * @param project 711 * @param projectVersion 712 * @param metadataFacet will remove artifacts which have this {@link MetadataFacet} using equals 713 * @throws MetadataRepositoryException 714 */ 715 public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion, 716 MetadataFacet metadataFacet ) 717 throws MetadataRepositoryException 718 { 719 throw new UnsupportedOperationException( "not implemented" ); 720 } 721 722 public void removeRepository( String repoId ) 723 { 724 File dir = getDirectory( repoId ); 725 if ( !FileUtils.deleteQuietly( dir ) ) 726 { 727 log.error( "Cannot delete repository {}", dir ); 728 } 729 } 730 731 private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns, 732 String checksum ) 733 { 734 for ( String namespace : getNamespaces( repositoryId, ns ) ) 735 { 736 getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum ); 737 } 738 739 for ( String project : getProjects( repositoryId, ns ) ) 740 { 741 for ( String version : getProjectVersions( repositoryId, ns, project ) ) 742 { 743 for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) ) 744 { 745 if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) ) 746 { 747 artifacts.add( artifact ); 748 } 749 } 750 } 751 } 752 } 753 754 private File getMetadataDirectory( String repoId, String facetId ) 755 { 756 return new File( getBaseDirectory( repoId ), "facets/" + facetId ); 757 } 758 759 private String join( Collection<String> ids ) 760 { 761 if ( ids != null && !ids.isEmpty() ) 762 { 763 StringBuilder s = new StringBuilder(); 764 for ( String id : ids ) 765 { 766 s.append( id ); 767 s.append( "," ); 768 } 769 return s.substring( 0, s.length() - 1 ); 770 } 771 return ""; 772 } 773 774 private void setProperty( Properties properties, String name, String value ) 775 { 776 if ( value != null ) 777 { 778 properties.setProperty( name, value ); 779 } 780 } 781 782 public void updateArtifact( String repoId, String namespace, String projectId, String projectVersion, 783 ArtifactMetadata artifact ) 784 { 785 ProjectVersionMetadata metadata = new ProjectVersionMetadata(); 786 metadata.setId( projectVersion ); 787 updateProjectVersion( repoId, namespace, projectId, metadata ); 788 789 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion ); 790 791 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 792 793 clearMetadataFacetProperties( artifact.getFacetList(), properties, "artifact:facet:" + artifact.getId() + ":" ); 794 795 String id = artifact.getId(); 796 properties.setProperty( "artifact:updated:" + id, Long.toString( artifact.getFileLastModified().getTime() ) ); 797 properties.setProperty( "artifact:whenGathered:" + id, Long.toString( artifact.getWhenGathered().getTime() ) ); 798 properties.setProperty( "artifact:size:" + id, Long.toString( artifact.getSize() ) ); 799 if ( artifact.getMd5() != null ) 800 { 801 properties.setProperty( "artifact:md5:" + id, artifact.getMd5() ); 802 } 803 if ( artifact.getSha1() != null ) 804 { 805 properties.setProperty( "artifact:sha1:" + id, artifact.getSha1() ); 806 } 807 properties.setProperty( "artifact:version:" + id, artifact.getVersion() ); 808 809 Set<String> facetIds = new LinkedHashSet<String>( artifact.getFacetIds() ); 810 String property = "artifact:facetIds:" + id; 811 facetIds.addAll( Arrays.asList( properties.getProperty( property, "" ).split( "," ) ) ); 812 properties.setProperty( property, join( facetIds ) ); 813 814 updateArtifactFacets( artifact, properties ); 815 816 try 817 { 818 writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); 819 } 820 catch ( IOException e ) 821 { 822 // TODO 823 log.error( e.getMessage(), e ); 824 } 825 } 826 827 private Properties readOrCreateProperties( File directory, String propertiesKey ) 828 { 829 try 830 { 831 return readProperties( directory, propertiesKey ); 832 } 833 catch ( FileNotFoundException e ) 834 { 835 // ignore and return new properties 836 } 837 catch ( IOException e ) 838 { 839 // TODO 840 log.error( e.getMessage(), e ); 841 } 842 return new Properties(); 843 } 844 845 private Properties readProperties( File directory, String propertiesKey ) 846 throws IOException 847 { 848 Properties properties = new Properties(); 849 FileInputStream in = null; 850 try 851 { 852 in = new FileInputStream( new File( directory, propertiesKey + ".properties" ) ); 853 properties.load( in ); 854 } 855 finally 856 { 857 IOUtils.closeQuietly( in ); 858 } 859 return properties; 860 } 861 862 public ProjectMetadata getProject( String repoId, String namespace, String projectId ) 863 { 864 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId ); 865 866 Properties properties = readOrCreateProperties( directory, PROJECT_METADATA_KEY ); 867 868 ProjectMetadata project = null; 869 870 String id = properties.getProperty( "id" ); 871 if ( id != null ) 872 { 873 project = new ProjectMetadata(); 874 project.setNamespace( properties.getProperty( "namespace" ) ); 875 project.setId( id ); 876 } 877 878 return project; 879 } 880 881 public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId, 882 String projectVersion ) 883 { 884 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion ); 885 886 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 887 String id = properties.getProperty( "id" ); 888 ProjectVersionMetadata versionMetadata = null; 889 if ( id != null ) 890 { 891 versionMetadata = new ProjectVersionMetadata(); 892 versionMetadata.setId( id ); 893 versionMetadata.setName( properties.getProperty( "name" ) ); 894 versionMetadata.setDescription( properties.getProperty( "description" ) ); 895 versionMetadata.setUrl( properties.getProperty( "url" ) ); 896 versionMetadata.setIncomplete( Boolean.valueOf( properties.getProperty( "incomplete", "false" ) ) ); 897 898 String scmConnection = properties.getProperty( "scm.connection" ); 899 String scmDeveloperConnection = properties.getProperty( "scm.developerConnection" ); 900 String scmUrl = properties.getProperty( "scm.url" ); 901 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null ) 902 { 903 Scm scm = new Scm(); 904 scm.setConnection( scmConnection ); 905 scm.setDeveloperConnection( scmDeveloperConnection ); 906 scm.setUrl( scmUrl ); 907 versionMetadata.setScm( scm ); 908 } 909 910 String ciSystem = properties.getProperty( "ci.system" ); 911 String ciUrl = properties.getProperty( "ci.url" ); 912 if ( ciSystem != null || ciUrl != null ) 913 { 914 CiManagement ci = new CiManagement(); 915 ci.setSystem( ciSystem ); 916 ci.setUrl( ciUrl ); 917 versionMetadata.setCiManagement( ci ); 918 } 919 920 String issueSystem = properties.getProperty( "issue.system" ); 921 String issueUrl = properties.getProperty( "issue.url" ); 922 if ( issueSystem != null || issueUrl != null ) 923 { 924 IssueManagement issueManagement = new IssueManagement(); 925 issueManagement.setSystem( issueSystem ); 926 issueManagement.setUrl( issueUrl ); 927 versionMetadata.setIssueManagement( issueManagement ); 928 } 929 930 String orgName = properties.getProperty( "org.name" ); 931 String orgUrl = properties.getProperty( "org.url" ); 932 if ( orgName != null || orgUrl != null ) 933 { 934 Organization org = new Organization(); 935 org.setName( orgName ); 936 org.setUrl( orgUrl ); 937 versionMetadata.setOrganization( org ); 938 } 939 940 boolean done = false; 941 int i = 0; 942 while ( !done ) 943 { 944 String licenseName = properties.getProperty( "license." + i + ".name" ); 945 String licenseUrl = properties.getProperty( "license." + i + ".url" ); 946 if ( licenseName != null || licenseUrl != null ) 947 { 948 License license = new License(); 949 license.setName( licenseName ); 950 license.setUrl( licenseUrl ); 951 versionMetadata.addLicense( license ); 952 } 953 else 954 { 955 done = true; 956 } 957 i++; 958 } 959 960 done = false; 961 i = 0; 962 while ( !done ) 963 { 964 String mailingListName = properties.getProperty( "mailingList." + i + ".name" ); 965 if ( mailingListName != null ) 966 { 967 MailingList mailingList = new MailingList(); 968 mailingList.setName( mailingListName ); 969 mailingList.setMainArchiveUrl( properties.getProperty( "mailingList." + i + ".archive" ) ); 970 String p = properties.getProperty( "mailingList." + i + ".otherArchives" ); 971 if ( p != null && p.length() > 0 ) 972 { 973 mailingList.setOtherArchives( Arrays.asList( p.split( "," ) ) ); 974 } 975 else 976 { 977 mailingList.setOtherArchives( Collections.<String>emptyList() ); 978 } 979 mailingList.setPostAddress( properties.getProperty( "mailingList." + i + ".post" ) ); 980 mailingList.setSubscribeAddress( properties.getProperty( "mailingList." + i + ".subscribe" ) ); 981 mailingList.setUnsubscribeAddress( properties.getProperty( "mailingList." + i + ".unsubscribe" ) ); 982 versionMetadata.addMailingList( mailingList ); 983 } 984 else 985 { 986 done = true; 987 } 988 i++; 989 } 990 991 done = false; 992 i = 0; 993 while ( !done ) 994 { 995 String dependencyArtifactId = properties.getProperty( "dependency." + i + ".artifactId" ); 996 if ( dependencyArtifactId != null ) 997 { 998 Dependency dependency = new Dependency(); 999 dependency.setArtifactId( dependencyArtifactId ); 1000 dependency.setGroupId( properties.getProperty( "dependency." + i + ".groupId" ) ); 1001 dependency.setClassifier( properties.getProperty( "dependency." + i + ".classifier" ) ); 1002 dependency.setOptional( 1003 Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) ); 1004 dependency.setScope( properties.getProperty( "dependency." + i + ".scope" ) ); 1005 dependency.setSystemPath( properties.getProperty( "dependency." + i + ".systemPath" ) ); 1006 dependency.setType( properties.getProperty( "dependency." + i + ".type" ) ); 1007 dependency.setVersion( properties.getProperty( "dependency." + i + ".version" ) ); 1008 dependency.setOptional( 1009 Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) ); 1010 versionMetadata.addDependency( dependency ); 1011 } 1012 else 1013 { 1014 done = true; 1015 } 1016 i++; 1017 } 1018 1019 String facetIds = properties.getProperty( "facetIds", "" ); 1020 if ( facetIds.length() > 0 ) 1021 { 1022 for ( String facetId : facetIds.split( "," ) ) 1023 { 1024 MetadataFacetFactory factory = metadataFacetFactories.get( facetId ); 1025 if ( factory == null ) 1026 { 1027 log.error( "Attempted to load unknown project version metadata facet: {}", facetId ); 1028 } 1029 else 1030 { 1031 MetadataFacet facet = factory.createMetadataFacet(); 1032 Map<String, String> map = new HashMap<String, String>(); 1033 for ( Object key : new ArrayList( properties.keySet() ) ) 1034 { 1035 String property = (String) key; 1036 if ( property.startsWith( facet.getFacetId() ) ) 1037 { 1038 map.put( property.substring( facet.getFacetId().length() + 1 ), 1039 properties.getProperty( property ) ); 1040 } 1041 } 1042 facet.fromProperties( map ); 1043 versionMetadata.addFacet( facet ); 1044 } 1045 } 1046 } 1047 1048 updateProjectVersionFacets( versionMetadata, properties ); 1049 } 1050 return versionMetadata; 1051 } 1052 1053 public Collection<String> getArtifactVersions( String repoId, String namespace, String projectId, 1054 String projectVersion ) 1055 { 1056 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion ); 1057 1058 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 1059 1060 Set<String> versions = new HashSet<String>(); 1061 for ( Map.Entry entry : properties.entrySet() ) 1062 { 1063 String name = (String) entry.getKey(); 1064 if ( name.startsWith( "artifact:version:" ) ) 1065 { 1066 versions.add( (String) entry.getValue() ); 1067 } 1068 } 1069 return versions; 1070 } 1071 1072 public Collection<ProjectVersionReference> getProjectReferences( String repoId, String namespace, String projectId, 1073 String projectVersion ) 1074 { 1075 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion ); 1076 1077 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); 1078 int numberOfRefs = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1; 1079 1080 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>(); 1081 for ( int i = 0; i < numberOfRefs; i++ ) 1082 { 1083 ProjectVersionReference reference = new ProjectVersionReference(); 1084 reference.setProjectId( properties.getProperty( "ref:reference." + i + ".projectId" ) ); 1085 reference.setNamespace( properties.getProperty( "ref:reference." + i + ".namespace" ) ); 1086 reference.setProjectVersion( properties.getProperty( "ref:reference." + i + ".projectVersion" ) ); 1087 reference.setReferenceType( ProjectVersionReference.ReferenceType.valueOf( 1088 properties.getProperty( "ref:reference." + i + ".referenceType" ) ) ); 1089 references.add( reference ); 1090 } 1091 return references; 1092 } 1093 1094 public Collection<String> getRootNamespaces( String repoId ) 1095 { 1096 return getNamespaces( repoId, null ); 1097 } 1098 1099 public Collection<String> getNamespaces( String repoId, String baseNamespace ) 1100 { 1101 List<String> allNamespaces = new ArrayList<String>(); 1102 File directory = getDirectory( repoId ); 1103 File[] files = directory.listFiles(); 1104 if ( files != null ) 1105 { 1106 for ( File namespace : files ) 1107 { 1108 if ( new File( namespace, NAMESPACE_METADATA_KEY + ".properties" ).exists() ) 1109 { 1110 allNamespaces.add( namespace.getName() ); 1111 } 1112 } 1113 } 1114 1115 Set<String> namespaces = new LinkedHashSet<String>(); 1116 int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0; 1117 for ( String namespace : allNamespaces ) 1118 { 1119 if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) ) 1120 { 1121 int i = namespace.indexOf( '.', fromIndex ); 1122 if ( i >= 0 ) 1123 { 1124 namespaces.add( namespace.substring( fromIndex, i ) ); 1125 } 1126 else 1127 { 1128 namespaces.add( namespace.substring( fromIndex ) ); 1129 } 1130 } 1131 } 1132 return new ArrayList<String>( namespaces ); 1133 } 1134 1135 public Collection<String> getProjects( String repoId, String namespace ) 1136 { 1137 List<String> projects = new ArrayList<String>(); 1138 File directory = new File( getDirectory( repoId ), namespace ); 1139 File[] files = directory.listFiles(); 1140 if ( files != null ) 1141 { 1142 for ( File project : files ) 1143 { 1144 if ( new File( project, PROJECT_METADATA_KEY + ".properties" ).exists() ) 1145 { 1146 projects.add( project.getName() ); 1147 } 1148 } 1149 } 1150 return projects; 1151 } 1152 1153 public Collection<String> getProjectVersions( String repoId, String namespace, String projectId ) 1154 { 1155 List<String> projectVersions = new ArrayList<String>(); 1156 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId ); 1157 File[] files = directory.listFiles(); 1158 if ( files != null ) 1159 { 1160 for ( File projectVersion : files ) 1161 { 1162 if ( new File( projectVersion, PROJECT_VERSION_METADATA_KEY + ".properties" ).exists() ) 1163 { 1164 projectVersions.add( projectVersion.getName() ); 1165 } 1166 } 1167 } 1168 return projectVersions; 1169 } 1170 1171 public void removeProject( String repositoryId, String namespace, String projectId ) 1172 throws MetadataRepositoryException 1173 { 1174 File directory = new File( getDirectory( repositoryId ), namespace + "/" + projectId ); 1175 try 1176 { 1177 if ( directory.exists() ) 1178 { 1179 FileUtils.deleteDirectory( directory ); 1180 } 1181 } 1182 catch ( IOException e ) 1183 { 1184 throw new MetadataRepositoryException( e.getMessage(), e ); 1185 } 1186 } 1187 1188 public void removeProjectVersion( String repoId, String namespace, String projectId, String projectVersion ) 1189 throws MetadataRepositoryException 1190 { 1191 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion ); 1192 if ( directory.exists() ) 1193 { 1194 try 1195 { 1196 FileUtils.deleteDirectory( directory ); 1197 } 1198 catch ( IOException e ) 1199 { 1200 throw new MetadataRepositoryException( e.getMessage(), e ); 1201 } 1202 } 1203 } 1204 1205 private void writeProperties( Properties properties, File directory, String propertiesKey ) 1206 throws IOException 1207 { 1208 directory.mkdirs(); 1209 FileOutputStream os = new FileOutputStream( new File( directory, propertiesKey + ".properties" ) ); 1210 try 1211 { 1212 properties.store( os, null ); 1213 } 1214 finally 1215 { 1216 IOUtils.closeQuietly( os ); 1217 } 1218 } 1219 1220 private static class ArtifactComparator 1221 implements Comparator<ArtifactMetadata> 1222 { 1223 public int compare( ArtifactMetadata artifact1, ArtifactMetadata artifact2 ) 1224 { 1225 if ( artifact1.getWhenGathered() == artifact2.getWhenGathered() ) 1226 { 1227 return 0; 1228 } 1229 if ( artifact1.getWhenGathered() == null ) 1230 { 1231 return 1; 1232 } 1233 if ( artifact2.getWhenGathered() == null ) 1234 { 1235 return -1; 1236 } 1237 return artifact1.getWhenGathered().compareTo( artifact2.getWhenGathered() ); 1238 } 1239 } 1240 1241 public List<ArtifactMetadata> getArtifacts( String repoId ) 1242 { 1243 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>(); 1244 for ( String ns : getRootNamespaces( repoId ) ) 1245 { 1246 getArtifacts( artifacts, repoId, ns ); 1247 } 1248 return artifacts; 1249 } 1250 1251 private void getArtifacts( List<ArtifactMetadata> artifacts, String repoId, String ns ) 1252 { 1253 for ( String namespace : getNamespaces( repoId, ns ) ) 1254 { 1255 getArtifacts( artifacts, repoId, ns + "." + namespace ); 1256 } 1257 1258 for ( String project : getProjects( repoId, ns ) ) 1259 { 1260 for ( String version : getProjectVersions( repoId, ns, project ) ) 1261 { 1262 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) ) 1263 { 1264 artifacts.add( artifact ); 1265 } 1266 } 1267 } 1268 } 1269 }