mirror of https://github.com/apache/archiva.git
[MRM-9] add transactional capabilities
git-svn-id: https://svn.apache.org/repos/asf/maven/repository-manager/trunk@373039 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
fa1de2a3e6
commit
be9c26c8cc
|
@ -28,6 +28,7 @@ import org.apache.maven.artifact.repository.metadata.Versioning;
|
|||
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader;
|
||||
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
|
||||
import org.apache.maven.model.converter.ArtifactPomRewriter;
|
||||
import org.apache.maven.repository.converter.transaction.FileTransaction;
|
||||
import org.apache.maven.repository.digest.Digester;
|
||||
import org.apache.maven.repository.reporting.ArtifactReporter;
|
||||
import org.codehaus.plexus.i18n.I18N;
|
||||
|
@ -37,10 +38,9 @@ import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.Writer;
|
||||
import java.io.StringWriter;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -96,34 +96,43 @@ public class DefaultRepositoryConverter
|
|||
|
||||
if ( validateMetadata( artifact, reporter ) )
|
||||
{
|
||||
if ( copyArtifact( artifact, targetRepository, reporter ) )
|
||||
FileTransaction transaction = new FileTransaction();
|
||||
|
||||
if ( copyArtifact( artifact, targetRepository, reporter, transaction ) )
|
||||
{
|
||||
copyPom( artifact, targetRepository, reporter );
|
||||
|
||||
Metadata metadata = createBaseMetadata( artifact );
|
||||
Versioning versioning = new Versioning();
|
||||
versioning.addVersion( artifact.getBaseVersion() );
|
||||
metadata.setVersioning( versioning );
|
||||
updateMetadata( new ArtifactRepositoryMetadata( artifact ), targetRepository, metadata );
|
||||
|
||||
metadata = createBaseMetadata( artifact );
|
||||
metadata.setVersion( artifact.getBaseVersion() );
|
||||
versioning = new Versioning();
|
||||
|
||||
Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher( artifact.getVersion() );
|
||||
if ( matcher.matches() )
|
||||
if ( copyPom( artifact, targetRepository, reporter, transaction ) )
|
||||
{
|
||||
Snapshot snapshot = new Snapshot();
|
||||
snapshot.setBuildNumber( Integer.valueOf( matcher.group( 3 ) ).intValue() );
|
||||
snapshot.setTimestamp( matcher.group( 2 ) );
|
||||
versioning.setSnapshot( snapshot );
|
||||
Metadata metadata = createBaseMetadata( artifact );
|
||||
Versioning versioning = new Versioning();
|
||||
versioning.addVersion( artifact.getBaseVersion() );
|
||||
metadata.setVersioning( versioning );
|
||||
updateMetadata( new ArtifactRepositoryMetadata( artifact ), targetRepository, metadata,
|
||||
transaction );
|
||||
|
||||
metadata = createBaseMetadata( artifact );
|
||||
metadata.setVersion( artifact.getBaseVersion() );
|
||||
versioning = new Versioning();
|
||||
|
||||
Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher( artifact.getVersion() );
|
||||
if ( matcher.matches() )
|
||||
{
|
||||
Snapshot snapshot = new Snapshot();
|
||||
snapshot.setBuildNumber( Integer.valueOf( matcher.group( 3 ) ).intValue() );
|
||||
snapshot.setTimestamp( matcher.group( 2 ) );
|
||||
versioning.setSnapshot( snapshot );
|
||||
}
|
||||
|
||||
// TODO: merge latest/release/snapshot from source instead
|
||||
metadata.setVersioning( versioning );
|
||||
updateMetadata( new SnapshotArtifactRepositoryMetadata( artifact ), targetRepository, metadata,
|
||||
transaction );
|
||||
|
||||
if ( !dryrun )
|
||||
{
|
||||
transaction.commit();
|
||||
}
|
||||
reporter.addSuccess( artifact );
|
||||
}
|
||||
|
||||
// TODO: merge latest/release/snapshot from source instead
|
||||
metadata.setVersioning( versioning );
|
||||
updateMetadata( new SnapshotArtifactRepositoryMetadata( artifact ), targetRepository, metadata );
|
||||
|
||||
reporter.addSuccess( artifact );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +146,7 @@ public class DefaultRepositoryConverter
|
|||
}
|
||||
|
||||
private void updateMetadata( RepositoryMetadata artifactMetadata, ArtifactRepository targetRepository,
|
||||
Metadata newMetadata )
|
||||
Metadata newMetadata, FileTransaction transaction )
|
||||
throws RepositoryConversionException
|
||||
{
|
||||
File file = new File( targetRepository.getBasedir(),
|
||||
|
@ -157,17 +166,18 @@ public class DefaultRepositoryConverter
|
|||
metadata = newMetadata;
|
||||
}
|
||||
|
||||
if ( changed && !dryrun )
|
||||
if ( changed )
|
||||
{
|
||||
Writer writer = null;
|
||||
StringWriter writer = null;
|
||||
try
|
||||
{
|
||||
file.getParentFile().mkdirs();
|
||||
writer = new FileWriter( file );
|
||||
writer = new StringWriter();
|
||||
|
||||
MetadataXpp3Writer mappingWriter = new MetadataXpp3Writer();
|
||||
|
||||
mappingWriter.write( writer, metadata );
|
||||
|
||||
transaction.createFile( writer.toString(), file );
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
|
@ -327,7 +337,8 @@ public class DefaultRepositoryConverter
|
|||
return result;
|
||||
}
|
||||
|
||||
private void copyPom( Artifact artifact, ArtifactRepository targetRepository, ArtifactReporter reporter )
|
||||
private boolean copyPom( Artifact artifact, ArtifactRepository targetRepository, ArtifactReporter reporter,
|
||||
FileTransaction transaction )
|
||||
throws RepositoryConversionException
|
||||
{
|
||||
Artifact pom = artifactFactory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(),
|
||||
|
@ -336,6 +347,7 @@ public class DefaultRepositoryConverter
|
|||
ArtifactRepository repository = artifact.getRepository();
|
||||
File file = new File( repository.getBasedir(), repository.pathOf( pom ) );
|
||||
|
||||
boolean result = true;
|
||||
if ( file.exists() )
|
||||
{
|
||||
// TODO: utility methods in the model converter
|
||||
|
@ -369,11 +381,7 @@ public class DefaultRepositoryConverter
|
|||
}
|
||||
if ( force || !matching )
|
||||
{
|
||||
if ( !dryrun )
|
||||
{
|
||||
targetFile.getParentFile().mkdirs();
|
||||
FileUtils.fileWrite( targetFile.getAbsolutePath(), contents );
|
||||
}
|
||||
transaction.createFile( contents, targetFile );
|
||||
}
|
||||
}
|
||||
catch ( IOException e )
|
||||
|
@ -385,15 +393,17 @@ public class DefaultRepositoryConverter
|
|||
{
|
||||
// v3 POM
|
||||
StringReader stringReader = new StringReader( contents );
|
||||
Writer fileWriter = null;
|
||||
StringWriter writer = null;
|
||||
try
|
||||
{
|
||||
fileWriter = new FileWriter( targetFile );
|
||||
writer = new StringWriter();
|
||||
|
||||
// TODO: this api could be improved - is it worth having or go back to modelConverter?
|
||||
rewriter.rewrite( stringReader, fileWriter, false, artifact.getGroupId(), artifact.getArtifactId(),
|
||||
rewriter.rewrite( stringReader, writer, false, artifact.getGroupId(), artifact.getArtifactId(),
|
||||
artifact.getVersion(), artifact.getType() );
|
||||
|
||||
transaction.createFile( writer.toString(), targetFile );
|
||||
|
||||
List warnings = rewriter.getWarnings();
|
||||
|
||||
for ( Iterator i = warnings.iterator(); i.hasNext(); )
|
||||
|
@ -401,24 +411,32 @@ public class DefaultRepositoryConverter
|
|||
String message = (String) i.next();
|
||||
reporter.addWarning( artifact, message );
|
||||
}
|
||||
|
||||
IOUtil.close( fileWriter );
|
||||
}
|
||||
catch ( XmlPullParserException e )
|
||||
{
|
||||
reporter.addFailure( artifact, getI18NString( "failure.invalid.source.pom", e.getMessage() ) );
|
||||
result = false;
|
||||
}
|
||||
catch ( Exception e )
|
||||
{
|
||||
if ( fileWriter != null )
|
||||
{
|
||||
IOUtil.close( fileWriter );
|
||||
targetFile.delete();
|
||||
}
|
||||
throw new RepositoryConversionException( "Unable to write converted POM", e );
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close( writer );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reporter.addWarning( artifact, getI18NString( "warning.missing.pom" ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getI18NString( String key, String arg0 )
|
||||
{
|
||||
return i18n.format( getClass().getName(), Locale.getDefault(), key, arg0 );
|
||||
}
|
||||
|
||||
private String getI18NString( String key )
|
||||
|
@ -462,7 +480,8 @@ public class DefaultRepositoryConverter
|
|||
return result;
|
||||
}
|
||||
|
||||
private boolean copyArtifact( Artifact artifact, ArtifactRepository targetRepository, ArtifactReporter reporter )
|
||||
private boolean copyArtifact( Artifact artifact, ArtifactRepository targetRepository, ArtifactReporter reporter,
|
||||
FileTransaction transaction )
|
||||
throws RepositoryConversionException
|
||||
{
|
||||
File sourceFile = artifact.getFile();
|
||||
|
@ -488,10 +507,7 @@ public class DefaultRepositoryConverter
|
|||
{
|
||||
if ( testChecksums( artifact, sourceFile, reporter ) )
|
||||
{
|
||||
if ( !dryrun )
|
||||
{
|
||||
FileUtils.copyFile( sourceFile, targetFile );
|
||||
}
|
||||
transaction.copyFile( sourceFile, targetFile );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package org.apache.maven.repository.converter.transaction;
|
||||
|
||||
/*
|
||||
* Copyright 2005-2006 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.codehaus.plexus.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Event to copy a file.
|
||||
*
|
||||
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
|
||||
*/
|
||||
public class CopyFileEvent
|
||||
implements TransactionEvent
|
||||
{
|
||||
private final File source;
|
||||
|
||||
private final File destination;
|
||||
|
||||
public CopyFileEvent( File source, File destination )
|
||||
{
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
public void commit()
|
||||
throws IOException
|
||||
{
|
||||
destination.getParentFile().mkdirs();
|
||||
|
||||
FileUtils.copyFile( source, destination );
|
||||
}
|
||||
|
||||
public void rollback()
|
||||
throws IOException
|
||||
{
|
||||
// TODO: revert to backup/delete if was created
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package org.apache.maven.repository.converter.transaction;
|
||||
|
||||
/*
|
||||
* Copyright 2005-2006 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.codehaus.plexus.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Event for creating a file from a string content.
|
||||
*
|
||||
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
|
||||
*/
|
||||
public class CreateFileEvent
|
||||
implements TransactionEvent
|
||||
{
|
||||
private final File destination;
|
||||
|
||||
private final String content;
|
||||
|
||||
public CreateFileEvent( String content, File destination )
|
||||
{
|
||||
this.content = content;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
public void commit()
|
||||
throws IOException
|
||||
{
|
||||
destination.getParentFile().mkdirs();
|
||||
|
||||
FileUtils.fileWrite( destination.getAbsolutePath(), content );
|
||||
}
|
||||
|
||||
public void rollback()
|
||||
throws IOException
|
||||
{
|
||||
// TODO: revert to backup/delete if was created
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package org.apache.maven.repository.converter.transaction;
|
||||
|
||||
/*
|
||||
* Copyright 2005-2006 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.apache.maven.repository.converter.RepositoryConversionException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implement commit/rollback semantics for a set of files.
|
||||
*
|
||||
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
|
||||
*/
|
||||
public class FileTransaction
|
||||
{
|
||||
private List events = new ArrayList();
|
||||
|
||||
public void commit()
|
||||
throws RepositoryConversionException
|
||||
{
|
||||
List toRollback = new ArrayList( events.size() );
|
||||
|
||||
for ( Iterator i = events.iterator(); i.hasNext(); )
|
||||
{
|
||||
TransactionEvent event = (TransactionEvent) i.next();
|
||||
|
||||
try
|
||||
{
|
||||
event.commit();
|
||||
|
||||
toRollback.add( event );
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
try
|
||||
{
|
||||
rollback( toRollback );
|
||||
|
||||
throw new RepositoryConversionException( "Unable to commit file transaction", e );
|
||||
}
|
||||
catch ( IOException ioe )
|
||||
{
|
||||
throw new RepositoryConversionException(
|
||||
"Unable to commit file transaction, and rollback failed with error: '" + ioe.getMessage() + "'",
|
||||
e );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rollback( List toRollback )
|
||||
throws IOException
|
||||
{
|
||||
for ( Iterator i = toRollback.iterator(); i.hasNext(); )
|
||||
{
|
||||
TransactionEvent event = (TransactionEvent) i.next();
|
||||
|
||||
event.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
public void copyFile( File source, File destination )
|
||||
{
|
||||
events.add( new CopyFileEvent( source, destination ) );
|
||||
}
|
||||
|
||||
public void createFile( String content, File destination )
|
||||
{
|
||||
events.add( new CreateFileEvent( content, destination ) );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package org.apache.maven.repository.converter.transaction;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* Copyright 2005-2006 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for individual events in a transaction.
|
||||
*
|
||||
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
|
||||
*/
|
||||
public interface TransactionEvent
|
||||
{
|
||||
/**
|
||||
* Commit this event.
|
||||
*
|
||||
* @throws IOException if an error occurred committing the change
|
||||
*/
|
||||
void commit()
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Rollback the even already committed.
|
||||
*
|
||||
* @throws IOException if an error occurred reverting the change
|
||||
*/
|
||||
void rollback()
|
||||
throws IOException;
|
||||
}
|
|
@ -16,8 +16,11 @@
|
|||
|
||||
failure.incorrect.md5=The MD5 checksum value was incorrect.
|
||||
failure.incorrect.sha1=The SHA1 checksum value was incorrect.
|
||||
warning.missing.pom=The artifact had no POM in the source repository.
|
||||
failure.target.already.exists=The artifact could not be converted because it already exists.
|
||||
failure.invalid.source.pom=The source POM was invalid: {0}.
|
||||
|
||||
warning.missing.pom=The artifact had no POM in the source repository.
|
||||
|
||||
exception.repositories.match=Source and target repositories are identical.
|
||||
|
||||
failure.incorrect.groupMetadata.groupId=The group ID in the source group metadata is incorrect.
|
||||
|
|
|
@ -643,31 +643,30 @@ public class RepositoryConverterTest
|
|||
}
|
||||
|
||||
public void testRollbackArtifactCreated()
|
||||
throws RepositoryConversionException, IOException
|
||||
{
|
||||
// test rollback can remove a created artifact, including checksums
|
||||
|
||||
// TODO
|
||||
}
|
||||
Artifact artifact = createArtifact( "test", "rollback-created-artifact", "1.0.0" );
|
||||
ArtifactMetadata artifactMetadata = new ArtifactRepositoryMetadata( artifact );
|
||||
File artifactMetadataFile = new File( targetRepository.getBasedir(),
|
||||
targetRepository.pathOfRemoteRepositoryMetadata( artifactMetadata ) );
|
||||
FileUtils.deleteDirectory( artifactMetadataFile.getParentFile() );
|
||||
|
||||
public void testRollbackArtifactChanged()
|
||||
{
|
||||
// test rollback can undo changes to an artifact, including checksums
|
||||
ArtifactMetadata versionMetadata = new SnapshotArtifactRepositoryMetadata( artifact );
|
||||
File versionMetadataFile = new File( targetRepository.getBasedir(),
|
||||
targetRepository.pathOfRemoteRepositoryMetadata( versionMetadata ) );
|
||||
|
||||
// TODO
|
||||
}
|
||||
File artifactFile = new File( targetRepository.getBasedir(), targetRepository.pathOf( artifact ) );
|
||||
|
||||
public void testRollbackMetadataCreated()
|
||||
{
|
||||
// test rollback can remove a created artifact's metadata, including checksums
|
||||
repositoryConverter.convert( artifact, targetRepository, reporter );
|
||||
checkFailure();
|
||||
String pattern = "^" + getI18nString( "failure.invalid.source.pom" ).replace( "{0}", "(.*?)" ) + "$";
|
||||
assertTrue( "Check failure message", getFailure().getReason().matches( pattern ) );
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void testRollbackMetadataChanged()
|
||||
{
|
||||
// test rollback can undo changes to an artifact's metadata, including checksums
|
||||
|
||||
// TODO
|
||||
assertFalse( "check artifact rolled back", artifactFile.exists() );
|
||||
assertFalse( "check metadata rolled back", artifactMetadataFile.exists() );
|
||||
assertFalse( "check metadata rolled back", versionMetadataFile.exists() );
|
||||
}
|
||||
|
||||
public void testMultipleArtifacts()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
v3
|
|
@ -0,0 +1,39 @@
|
|||
<!--
|
||||
~ Copyright 2005-2006 The Apache Software Foundation.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>v3artifact</artifactId>
|
||||
<groupId>test</groupId>
|
||||
<currentVersion>1.0.0</currentVersion>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>groupId</groupId>
|
||||
<artifactId>artifactId</artifactId>
|
||||
<version>version</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>groupId</groupId>
|
||||
<artifactId>test-artifactId</artifactId>
|
||||
<version>version</version>
|
||||
<properties>
|
||||
<scope>test</scope>
|
||||
</properties>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!-- deliberate parse error -->
|
||||
<repository>
|
||||
</project>
|
Loading…
Reference in New Issue