Browse Source

[MRM-9] add transactional capabilities

git-svn-id: https://svn.apache.org/repos/asf/maven/repository-manager/trunk@373039 13f79535-47bb-0310-9956-ffa450edef68
tags/archiva-0.9-alpha-1
Brett Porter 18 years ago
parent
commit
be9c26c8cc

+ 67
- 51
maven-repository-converter/src/main/java/org/apache/maven/repository/converter/DefaultRepositoryConverter.java 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,34 +96,43 @@ public class DefaultRepositoryConverter

if ( validateMetadata( artifact, reporter ) )
{
if ( copyArtifact( artifact, targetRepository, reporter ) )
{
copyPom( artifact, targetRepository, reporter );
FileTransaction transaction = new FileTransaction();

Metadata metadata = createBaseMetadata( artifact );
Versioning versioning = new Versioning();
versioning.addVersion( artifact.getBaseVersion() );
metadata.setVersioning( versioning );
updateMetadata( new ArtifactRepositoryMetadata( artifact ), targetRepository, metadata );
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,
transaction );

metadata = createBaseMetadata( artifact );
metadata.setVersion( artifact.getBaseVersion() );
versioning = new Versioning();
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 );
}
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 );
// TODO: merge latest/release/snapshot from source instead
metadata.setVersioning( versioning );
updateMetadata( new SnapshotArtifactRepositoryMetadata( artifact ), targetRepository, metadata,
transaction );

reporter.addSuccess( artifact );
if ( !dryrun )
{
transaction.commit();
}
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
{

+ 55
- 0
maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CopyFileEvent.java 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
}
}

+ 55
- 0
maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CreateFileEvent.java 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
}
}

+ 89
- 0
maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/FileTransaction.java 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 ) );
}
}

+ 43
- 0
maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/TransactionEvent.java 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;
}

+ 4
- 1
maven-repository-converter/src/main/resources/org/apache/maven/repository/converter/DefaultRepositoryConverter.properties 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.

+ 17
- 18
maven-repository-converter/src/test/java/org/apache/maven/repository/converter/RepositoryConverterTest.java 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
}

public void testRollbackArtifactChanged()
{
// test rollback can undo changes to an 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 testRollbackMetadataCreated()
{
// test rollback can remove a created artifact's metadata, 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 testRollbackMetadataChanged()
{
// test rollback can undo changes to an 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
assertFalse( "check artifact rolled back", artifactFile.exists() );
assertFalse( "check metadata rolled back", artifactMetadataFile.exists() );
assertFalse( "check metadata rolled back", versionMetadataFile.exists() );
}

public void testMultipleArtifacts()

+ 1
- 0
maven-repository-converter/src/test/source-repository/test/jars/rollback-created-artifact-1.0.0.jar View File

@@ -0,0 +1 @@
v3

+ 39
- 0
maven-repository-converter/src/test/source-repository/test/poms/rollback-created-artifact-1.0.0.pom 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>

Loading…
Cancel
Save