From be9c26c8cccfc149f3c8142924ed14fa8fb08696 Mon Sep 17 00:00:00 2001 From: Brett Porter Date: Sat, 28 Jan 2006 00:18:51 +0000 Subject: [PATCH] [MRM-9] add transactional capabilities git-svn-id: https://svn.apache.org/repos/asf/maven/repository-manager/trunk@373039 13f79535-47bb-0310-9956-ffa450edef68 --- .../converter/DefaultRepositoryConverter.java | 122 ++++++++++-------- .../converter/transaction/CopyFileEvent.java | 55 ++++++++ .../transaction/CreateFileEvent.java | 55 ++++++++ .../transaction/FileTransaction.java | 89 +++++++++++++ .../transaction/TransactionEvent.java | 43 ++++++ .../DefaultRepositoryConverter.properties | 5 +- .../converter/RepositoryConverterTest.java | 35 +++-- .../jars/rollback-created-artifact-1.0.0.jar | 1 + .../poms/rollback-created-artifact-1.0.0.pom | 39 ++++++ 9 files changed, 372 insertions(+), 72 deletions(-) create mode 100644 maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CopyFileEvent.java create mode 100644 maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CreateFileEvent.java create mode 100644 maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/FileTransaction.java create mode 100644 maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/TransactionEvent.java create mode 100644 maven-repository-converter/src/test/source-repository/test/jars/rollback-created-artifact-1.0.0.jar create mode 100644 maven-repository-converter/src/test/source-repository/test/poms/rollback-created-artifact-1.0.0.pom diff --git a/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/DefaultRepositoryConverter.java b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/DefaultRepositoryConverter.java index 01dcaf968..fa7608a75 100644 --- a/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/DefaultRepositoryConverter.java +++ b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/DefaultRepositoryConverter.java @@ -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 { diff --git a/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CopyFileEvent.java b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CopyFileEvent.java new file mode 100644 index 000000000..996aabd93 --- /dev/null +++ b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CopyFileEvent.java @@ -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 Brett Porter + */ +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 + } +} diff --git a/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CreateFileEvent.java b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CreateFileEvent.java new file mode 100644 index 000000000..79644a37d --- /dev/null +++ b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/CreateFileEvent.java @@ -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 Brett Porter + */ +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 + } +} diff --git a/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/FileTransaction.java b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/FileTransaction.java new file mode 100644 index 000000000..ceb368660 --- /dev/null +++ b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/FileTransaction.java @@ -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 Brett Porter + */ +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 ) ); + } +} diff --git a/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/TransactionEvent.java b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/TransactionEvent.java new file mode 100644 index 000000000..45b792992 --- /dev/null +++ b/maven-repository-converter/src/main/java/org/apache/maven/repository/converter/transaction/TransactionEvent.java @@ -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 Brett Porter + */ +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; +} diff --git a/maven-repository-converter/src/main/resources/org/apache/maven/repository/converter/DefaultRepositoryConverter.properties b/maven-repository-converter/src/main/resources/org/apache/maven/repository/converter/DefaultRepositoryConverter.properties index f55089dbb..f6089c1f5 100644 --- a/maven-repository-converter/src/main/resources/org/apache/maven/repository/converter/DefaultRepositoryConverter.properties +++ b/maven-repository-converter/src/main/resources/org/apache/maven/repository/converter/DefaultRepositoryConverter.properties @@ -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. diff --git a/maven-repository-converter/src/test/java/org/apache/maven/repository/converter/RepositoryConverterTest.java b/maven-repository-converter/src/test/java/org/apache/maven/repository/converter/RepositoryConverterTest.java index ad8363047..eaaf7cd8f 100644 --- a/maven-repository-converter/src/test/java/org/apache/maven/repository/converter/RepositoryConverterTest.java +++ b/maven-repository-converter/src/test/java/org/apache/maven/repository/converter/RepositoryConverterTest.java @@ -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() diff --git a/maven-repository-converter/src/test/source-repository/test/jars/rollback-created-artifact-1.0.0.jar b/maven-repository-converter/src/test/source-repository/test/jars/rollback-created-artifact-1.0.0.jar new file mode 100644 index 000000000..29ef827e8 --- /dev/null +++ b/maven-repository-converter/src/test/source-repository/test/jars/rollback-created-artifact-1.0.0.jar @@ -0,0 +1 @@ +v3 diff --git a/maven-repository-converter/src/test/source-repository/test/poms/rollback-created-artifact-1.0.0.pom b/maven-repository-converter/src/test/source-repository/test/poms/rollback-created-artifact-1.0.0.pom new file mode 100644 index 000000000..00692be72 --- /dev/null +++ b/maven-repository-converter/src/test/source-repository/test/poms/rollback-created-artifact-1.0.0.pom @@ -0,0 +1,39 @@ + + + + 3 + v3artifact + test + 1.0.0 + + + groupId + artifactId + version + + + groupId + test-artifactId + version + + test + + + + + +