[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:
Brett Porter 2006-01-28 00:18:51 +00:00
parent fa1de2a3e6
commit be9c26c8cc
9 changed files with 372 additions and 72 deletions

View File

@ -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,15 +96,18 @@ public class DefaultRepositoryConverter
if ( validateMetadata( artifact, reporter ) )
{
if ( copyArtifact( artifact, targetRepository, reporter ) )
{
copyPom( artifact, targetRepository, reporter );
FileTransaction transaction = new FileTransaction();
if ( copyArtifact( artifact, targetRepository, reporter, transaction ) )
{
if ( copyPom( artifact, targetRepository, reporter, transaction ) )
{
Metadata metadata = createBaseMetadata( artifact );
Versioning versioning = new Versioning();
versioning.addVersion( artifact.getBaseVersion() );
metadata.setVersioning( versioning );
updateMetadata( new ArtifactRepositoryMetadata( artifact ), targetRepository, metadata );
updateMetadata( new ArtifactRepositoryMetadata( artifact ), targetRepository, metadata,
transaction );
metadata = createBaseMetadata( artifact );
metadata.setVersion( artifact.getBaseVersion() );
@ -121,12 +124,18 @@ public class DefaultRepositoryConverter
// TODO: merge latest/release/snapshot from source instead
metadata.setVersioning( versioning );
updateMetadata( new SnapshotArtifactRepositoryMetadata( artifact ), targetRepository, metadata );
updateMetadata( new SnapshotArtifactRepositoryMetadata( artifact ), targetRepository, metadata,
transaction );
if ( !dryrun )
{
transaction.commit();
}
reporter.addSuccess( artifact );
}
}
}
}
private static Metadata createBaseMetadata( Artifact 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
{

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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 ) );
}
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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()

View File

@ -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>