/**
* Copyright (c) 2008-2011 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions.
*
* This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General
* Public License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License Version 3
* for more details.
*
* You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see
* http://www.gnu.org/licenses.
*
* Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of
* Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation.
* All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.proxy.maven;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.apache.maven.index.artifact.Gav;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.codehaus.plexus.util.StringUtils;
import org.sonatype.nexus.proxy.AccessDeniedException;
import org.sonatype.nexus.proxy.IllegalOperationException;
import org.sonatype.nexus.proxy.ItemNotFoundException;
import org.sonatype.nexus.proxy.LocalStorageException;
import org.sonatype.nexus.proxy.ResourceStoreRequest;
import org.sonatype.nexus.proxy.StorageException;
import org.sonatype.nexus.proxy.attributes.inspectors.DigestCalculatingInspector;
import org.sonatype.nexus.proxy.item.AbstractStorageItem;
import org.sonatype.nexus.proxy.item.DefaultStorageFileItem;
import org.sonatype.nexus.proxy.item.RepositoryItemUid;
import org.sonatype.nexus.proxy.item.StorageCollectionItem;
import org.sonatype.nexus.proxy.item.StorageFileItem;
import org.sonatype.nexus.proxy.item.StorageItem;
import org.sonatype.nexus.proxy.item.StringContentLocator;
import org.sonatype.nexus.proxy.storage.UnsupportedStorageOperationException;
/**
* Am ArtifactStore helper class, that simply drives a MavenRepository and gets various infos from it. It uses the
* Repository interface of it's "owner" repository for storing/retrieval.
*
* @author cstamas
*/
public class ArtifactStoreHelper
{
private final MavenRepository repository;
protected ArtifactStoreHelper( MavenRepository repo )
{
super();
this.repository = repo;
}
public MavenRepository getMavenRepository()
{
return repository;
}
public void storeItemWithChecksums( ResourceStoreRequest request, InputStream is, Map<String, String> userAttributes )
throws UnsupportedStorageOperationException, IllegalOperationException, StorageException, AccessDeniedException
{
String originalPath = request.getRequestPath();
try
{
try
{
getMavenRepository().storeItem( request, is, userAttributes );
}
catch ( IOException e )
{
throw new LocalStorageException( "Could not get the content from the ContentLocator!", e );
}
StorageFileItem storedFile = (StorageFileItem) getMavenRepository().retrieveItem( false, request );
String sha1Hash = storedFile.getAttributes().get( DigestCalculatingInspector.DIGEST_SHA1_KEY );
String md5Hash = storedFile.getAttributes().get( DigestCalculatingInspector.DIGEST_MD5_KEY );
if ( !StringUtils.isEmpty( sha1Hash ) )
{
request.setRequestPath( storedFile.getPath() + ".sha1" );
getMavenRepository().storeItem(
false,
new DefaultStorageFileItem( getMavenRepository(), request, true, true, new StringContentLocator(
sha1Hash ) ) );
}
if ( !StringUtils.isEmpty( md5Hash ) )
{
request.setRequestPath( storedFile.getPath() + ".md5" );
getMavenRepository().storeItem(
false,
new DefaultStorageFileItem( getMavenRepository(), request, true, true, new StringContentLocator(
md5Hash ) ) );
}
}
catch ( ItemNotFoundException e )
{
throw new LocalStorageException( "Storage inconsistency!", e );
}
finally
{
request.setRequestPath( originalPath );
}
}
public void deleteItemWithChecksums( ResourceStoreRequest request )
throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException,
StorageException, AccessDeniedException
{
try
{
getMavenRepository().deleteItem( request );
}
catch ( ItemNotFoundException e )
{
if ( request.getRequestPath().endsWith( ".asc" ) )
{
// Do nothing no guarantee that the .asc files will exist
}
else
{
throw e;
}
}
String originalPath = request.getRequestPath();
request.setRequestPath( originalPath + ".sha1" );
try
{
getMavenRepository().deleteItem( request );
}
catch ( ItemNotFoundException e )
{
// ignore not found
}
request.setRequestPath( originalPath + ".md5" );
try
{
getMavenRepository().deleteItem( request );
}
catch ( ItemNotFoundException e )
{
// ignore not found
}
// Now remove the .asc files, and the checksums stored with them as well
// Note this is a recursive call, hence the check for .asc
if ( !originalPath.endsWith( ".asc" ) )
{
request.setRequestPath( originalPath + ".asc" );
deleteItemWithChecksums( request );
}
}
public void storeItemWithChecksums( boolean fromTask, AbstractStorageItem item )
throws UnsupportedStorageOperationException, IllegalOperationException, StorageException
{
try
{
try
{
getMavenRepository().storeItem( false, item );
}
catch ( IOException e )
{
throw new LocalStorageException( "Could not get the content from the ContentLocator!", e );
}
StorageFileItem storedFile =
(StorageFileItem) getMavenRepository().retrieveItem( false, new ResourceStoreRequest( item ) );
ResourceStoreRequest req = new ResourceStoreRequest( storedFile );
String sha1Hash = storedFile.getAttributes().get( DigestCalculatingInspector.DIGEST_SHA1_KEY );
String md5Hash = storedFile.getAttributes().get( DigestCalculatingInspector.DIGEST_MD5_KEY );
if ( !StringUtils.isEmpty( sha1Hash ) )
{
req.setRequestPath( item.getPath() + ".sha1" );
getMavenRepository().storeItem(
false,
new DefaultStorageFileItem( getMavenRepository(), req, true, true, new StringContentLocator(
sha1Hash ) ) );
}
if ( !StringUtils.isEmpty( md5Hash ) )
{
req.setRequestPath( item.getPath() + ".md5" );
getMavenRepository().storeItem(
false,
new DefaultStorageFileItem( getMavenRepository(), req, true, true, new StringContentLocator(
md5Hash ) ) );
}
}
catch ( ItemNotFoundException e )
{
throw new LocalStorageException( "Storage inconsistency!", e );
}
}
public void deleteItemWithChecksums( boolean fromTask, ResourceStoreRequest request )
throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException, StorageException
{
try
{
getMavenRepository().deleteItem( fromTask, request );
}
catch ( ItemNotFoundException e )
{
if ( request.getRequestPath().endsWith( ".asc" ) )
{
// Do nothing no guarantee that the .asc files will exist
}
else
{
throw e;
}
}
try
{
request.pushRequestPath( request.getRequestPath() + ".sha1" );
try
{
getMavenRepository().deleteItem( fromTask, request );
}
catch ( ItemNotFoundException e )
{
// ignore not found
}
}
finally
{
request.popRequestPath();
}
try
{
request.pushRequestPath( request.getRequestPath() + ".md5" );
try
{
getMavenRepository().deleteItem( fromTask, request );
}
catch ( ItemNotFoundException e )
{
// ignore not found
}
}
finally
{
request.popRequestPath();
}
// Now remove the .asc files, and the checksums stored with them as well
// Note this is a recursive call, hence the check for .asc
if ( !request.getRequestPath().endsWith( ".asc" ) )
{
try
{
request.pushRequestPath( request.getRequestPath() + ".asc" );
deleteItemWithChecksums( fromTask, request );
}
finally
{
request.popRequestPath();
}
}
}
public StorageFileItem retrieveArtifactPom( ArtifactStoreRequest gavRequest )
throws IllegalOperationException, ItemNotFoundException, StorageException, AccessDeniedException
{
Gav pomGav =
new Gav(
gavRequest.getGav().getGroupId(),
gavRequest.getGav().getArtifactId(),
gavRequest.getGav().getVersion(),
null, // gavRequest.getGav().getClassifier(),
"pom", // gavRequest.getGav().getExtension(),
gavRequest.getGav().getSnapshotBuildNumber(), gavRequest.getGav().getSnapshotTimeStamp(),
gavRequest.getGav().getName(), gavRequest.getGav().isHash(), gavRequest.getGav().getHashType(),
gavRequest.getGav().isSignature(), gavRequest.getGav().getSignatureType() );
ArtifactStoreRequest pomRequest =
new ArtifactStoreRequest( gavRequest.getMavenRepository(), pomGav, gavRequest.isRequestLocalOnly(),
gavRequest.isRequestRemoteOnly() );
return retrieveArtifact( pomRequest );
}
public Gav resolveArtifact( ArtifactStoreRequest gavRequest )
throws IllegalOperationException, ItemNotFoundException, StorageException, AccessDeniedException
{
checkRequest( gavRequest );
try
{
Gav gav = repository.getMetadataManager().resolveArtifact( gavRequest );
if ( gav == null )
{
throw new ItemNotFoundException( "GAV: " + gavRequest.getGroupId() + " : " + gavRequest.getArtifactId()
+ " : " + gavRequest.getVersion(), gavRequest, repository );
}
return gav;
}
catch ( IOException e )
{
throw new LocalStorageException( "Could not maintain metadata!", e );
}
}
public StorageFileItem retrieveArtifact( ArtifactStoreRequest gavRequest )
throws IllegalOperationException, ItemNotFoundException, StorageException, AccessDeniedException
{
checkRequest( gavRequest );
Gav gav = resolveArtifact( gavRequest );
gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( gav ) );
StorageItem item = repository.retrieveItem( gavRequest );
if ( StorageFileItem.class.isAssignableFrom( item.getClass() ) )
{
return (StorageFileItem) item;
}
else
{
throw new LocalStorageException( "The Artifact retrieval returned non-file, path:"
+ item.getRepositoryItemUid().toString() );
}
}
public void storeArtifactPom( ArtifactStoreRequest gavRequest, InputStream is, Map<String, String> attributes )
throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException,
StorageException, AccessDeniedException
{
checkRequest( gavRequest );
Gav gav =
new Gav( gavRequest.getGroupId(), gavRequest.getArtifactId(), gavRequest.getVersion(),
gavRequest.getClassifier(), "pom", null, null, null, false, null, false, null );
gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( gav ) );
repository.storeItemWithChecksums( gavRequest, is, attributes );
try
{
repository.getMetadataManager().deployArtifact( gavRequest );
}
catch ( IOException e )
{
throw new LocalStorageException( "Could not maintain metadata!", e );
}
}
public void storeArtifact( ArtifactStoreRequest gavRequest, InputStream is, Map<String, String> attributes )
throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException,
StorageException, AccessDeniedException
{
checkRequest( gavRequest );
Gav gav =
new Gav( gavRequest.getGroupId(), gavRequest.getArtifactId(), gavRequest.getVersion(),
gavRequest.getClassifier(), gavRequest.getExtension(), null, null, null, false, null, false, null );
gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( gav ) );
repository.storeItemWithChecksums( gavRequest, is, attributes );
}
public void storeArtifactWithGeneratedPom( ArtifactStoreRequest gavRequest, String packaging, InputStream is,
Map<String, String> attributes )
throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException,
StorageException, AccessDeniedException
{
checkRequest( gavRequest );
// Force classifier to null, as the pom shouldn't have a classifier
Gav pomGav =
new Gav( gavRequest.getGroupId(), gavRequest.getArtifactId(), gavRequest.getVersion(), null, "pom", null,
null, null, false, null, false, null );
try
{
gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( pomGav ) );
// check for POM existence
repository.retrieveItem( false, gavRequest );
}
catch ( ItemNotFoundException e )
{
if ( StringUtils.isBlank( packaging ) )
{
throw new IllegalArgumentException( "Cannot generate POM without valid 'packaging'!" );
}
// POM does not exists
// generate minimal POM
// got from install:install-file plugin/mojo, thanks
Model model = new Model();
model.setModelVersion( "4.0.0" );
model.setGroupId( gavRequest.getGroupId() );
model.setArtifactId( gavRequest.getArtifactId() );
model.setVersion( gavRequest.getVersion() );
model.setPackaging( packaging );
model.setDescription( "POM was created by Sonatype Nexus" );
StringWriter sw = new StringWriter();
MavenXpp3Writer mw = new MavenXpp3Writer();
try
{
mw.write( sw, model );
}
catch ( IOException ex )
{
// writing to string, not to happen
}
gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( pomGav ) );
repository.storeItemWithChecksums( gavRequest, new ByteArrayInputStream( sw.toString().getBytes() ),
attributes );
try
{
repository.getMetadataManager().deployArtifact( gavRequest );
}
catch ( IOException ex )
{
throw new LocalStorageException( "Could not maintain metadata!", ex );
}
}
Gav artifactGav =
new Gav( gavRequest.getGroupId(), gavRequest.getArtifactId(), gavRequest.getVersion(),
gavRequest.getClassifier(), gavRequest.getExtension(), null, null, null, false, null, false, null );
gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( artifactGav ) );
repository.storeItemWithChecksums( gavRequest, is, attributes );
}
public void deleteArtifactPom( ArtifactStoreRequest gavRequest, boolean withChecksums, boolean withAllSubordinates,
boolean deleteWholeGav )
throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException,
StorageException, AccessDeniedException
{
// This is just so we can get teh gavToPath functionallity, to give us a path to work with
Gav gav =
new Gav( gavRequest.getGroupId(), gavRequest.getArtifactId(), gavRequest.getVersion(),
gavRequest.getClassifier(), "pom", null, null, null, false, null, false, null );
gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( gav ) );
/*
* // First undeploy, we will read the pom contents to build the gav try { gav = new Gav(
* gavRequest.getGroupId(), gavRequest.getArtifactId(), gavRequest.getVersion(), gavRequest.getClassifier(),
* getPackagingFromPom( gavRequest.getRequestPath() ), null, null, null, RepositoryPolicy.SNAPSHOT.equals(
* repository.getRepositoryPolicy() ), false, null, false, null ); } catch ( IOException e ) { throw new
* StorageException( "Could not read pom file!", e ); } catch ( XmlPullParserException e ) { throw new
* StorageException( "Could not read pom file!", e ); } gavRequest.setRequestPath(
* repository.getGavCalculator().gavToPath( gav ) ); // delete the pom's artifact handleDelete( gavRequest,
* deleteWholeGav, withChecksums, withAllSubordinates ); // Now delete the pom gav = new Gav(
* gavRequest.getGroupId(), gavRequest.getArtifactId(), gavRequest.getVersion(), gavRequest .getClassifier(),
* "pom", null, null, null, RepositoryPolicy.SNAPSHOT.equals( repository .getRepositoryPolicy() ), false, null,
* false, null ); gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( gav ) );
*/
handleDelete( gavRequest, deleteWholeGav, withChecksums, withAllSubordinates );
}
public void deleteArtifact( ArtifactStoreRequest gavRequest, boolean withChecksums, boolean withAllSubordinates,
boolean deleteWholeGav )
throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException,
StorageException, AccessDeniedException
{
// delete the artifact
Gav gav =
new Gav( gavRequest.getGroupId(), gavRequest.getArtifactId(), gavRequest.getVersion(),
gavRequest.getClassifier(), gavRequest.getExtension(), null, null, null, false, null, false, null );
gavRequest.setRequestPath( repository.getGavCalculator().gavToPath( gav ) );
handleDelete( gavRequest, deleteWholeGav, withChecksums, withAllSubordinates );
}
private void handleDelete( ArtifactStoreRequest gavRequest, boolean deleteWholeGav, boolean withChecksums,
boolean withAllSubordinates )
throws StorageException, UnsupportedStorageOperationException, IllegalOperationException,
AccessDeniedException, ItemNotFoundException
{
try
{
repository.getMetadataManager().undeployArtifact( gavRequest );
}
catch ( IOException e )
{
throw new LocalStorageException( "Could not maintain metadata!", e );
}
if ( deleteWholeGav )
{
deleteWholeGav( gavRequest );
}
else
{
if ( withChecksums )
{
repository.deleteItemWithChecksums( gavRequest );
}
else
{
repository.deleteItem( gavRequest );
}
if ( withAllSubordinates )
{
deleteAllSubordinates( gavRequest );
}
}
}
public Collection<Gav> listArtifacts( ArtifactStoreRequest gavRequest )
{
// TODO: implement this
return Collections.emptyList();
}
// =======================================================================================
protected void deleteAllSubordinates( ArtifactStoreRequest gavRequest )
throws UnsupportedStorageOperationException, IllegalOperationException, StorageException,
AccessDeniedException
{
// delete all "below", meaning: classifiers of the GAV
// watch for subdirs
// delete dir if empty
RepositoryItemUid parentCollUid =
repository.createUid( gavRequest.getRequestPath().substring( 0,
gavRequest.getRequestPath().indexOf( RepositoryItemUid.PATH_SEPARATOR ) ) );
try
{
gavRequest.setRequestPath( parentCollUid.getPath() );
// get the parent collection
StorageCollectionItem parentColl = (StorageCollectionItem) repository.retrieveItem( false, gavRequest );
// list it
Collection<StorageItem> items = repository.list( false, parentColl );
boolean hadSubdirectoryOrOtherFiles = false;
// and delete all except subdirs
for ( StorageItem item : items )
{
if ( !StorageCollectionItem.class.isAssignableFrom( item.getClass() ) )
{
Gav gav = repository.getGavCalculator().pathToGav( item.getPath() );
if ( gav != null && gavRequest.getGroupId().equals( gav.getGroupId() )
&& gavRequest.getArtifactId().equals( gav.getArtifactId() )
&& gavRequest.getVersion().equals( gav.getVersion() ) && gav.getClassifier() != null )
{
try
{
gavRequest.pushRequestPath( item.getPath() );
repository.deleteItem( false, gavRequest );
}
finally
{
gavRequest.popRequestPath();
}
}
else if ( !item.getPath().endsWith( "maven-metadata.xml" ) )
{
hadSubdirectoryOrOtherFiles = true;
}
}
else
{
hadSubdirectoryOrOtherFiles = true;
}
}
if ( !hadSubdirectoryOrOtherFiles )
{
repository.deleteItem( false, gavRequest );
}
}
catch ( ItemNotFoundException e )
{
// silent
}
}
protected void deleteWholeGav( ArtifactStoreRequest gavRequest )
throws UnsupportedStorageOperationException, IllegalOperationException, StorageException, AccessDeniedException
{
// delete all in this directory
// watch for subdirs
// delete dir if empty
RepositoryItemUid parentCollUid =
repository.createUid( gavRequest.getRequestPath().substring( 0,
gavRequest.getRequestPath().lastIndexOf( RepositoryItemUid.PATH_SEPARATOR ) ) );
try
{
gavRequest.setRequestPath( parentCollUid.getPath() );
// get the parent collection
StorageCollectionItem parentColl = (StorageCollectionItem) repository.retrieveItem( false, gavRequest );
// list it
Collection<StorageItem> items = repository.list( false, parentColl );
boolean hadSubdirectory = false;
// and delete all except subdirs
for ( StorageItem item : items )
{
if ( !StorageCollectionItem.class.isAssignableFrom( item.getClass() ) )
{
try
{
gavRequest.pushRequestPath( item.getPath() );
repository.deleteItem( false, gavRequest );
}
finally
{
gavRequest.popRequestPath();
}
}
else if ( !item.getPath().endsWith( "maven-metadata.xml" ) )
{
hadSubdirectory = true;
}
}
if ( !hadSubdirectory )
{
repository.deleteItem( false, gavRequest );
}
}
catch ( ItemNotFoundException e )
{
// silent
}
}
protected void checkRequest( ArtifactStoreRequest gavRequest )
{
if ( gavRequest.getGroupId() == null || gavRequest.getArtifactId() == null || gavRequest.getVersion() == null )
{
throw new IllegalArgumentException( "GAV is not supplied or only partially supplied! (G: '"
+ gavRequest.getGroupId() + "', A: '" + gavRequest.getArtifactId() + "', V: '"
+ gavRequest.getVersion() + "')" );
}
}
}