Author: ogusakov Date: Thu Sep 11 14:29:24 2008 New Revision: 694493 URL: http://svn.apache.org/viewvc?rev=694493&view=rev Log: mutli-threaded local repo write works somehow, breaks if too many threads on too fast a box try to bang it. No solution yet - posted a question on dev@ list
Added: maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileLockBundle.java Modified: maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/AbstractRepositoryWriterM2Test.java maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/LocalRepositoryWriterM2Test.java maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/RemoteRepositoryWriterM2NexusTest.java maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/LocalRepositoryWriterM2.java maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/Messages.properties maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileUtil.java maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/Messages.properties maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/FileUtilTest.java maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/NioTest.java Modified: maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/AbstractRepositoryWriterM2Test.java URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/AbstractRepositoryWriterM2Test.java?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/AbstractRepositoryWriterM2Test.java (original) +++ maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/AbstractRepositoryWriterM2Test.java Thu Sep 11 14:29:24 2008 @@ -1,6 +1,8 @@ package org.apache.maven.mercury.repository.tests; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileInputStream; import java.net.MalformedURLException; import java.util.HashSet; import java.util.Iterator; @@ -20,6 +22,8 @@ import org.apache.maven.mercury.repository.api.Repository; import org.apache.maven.mercury.repository.api.RepositoryReader; import org.apache.maven.mercury.repository.api.RepositoryWriter; +import org.apache.maven.mercury.repository.metadata.Metadata; +import org.apache.maven.mercury.repository.metadata.MetadataBuilder; import org.apache.maven.mercury.transport.api.Server; import org.apache.maven.mercury.util.FileUtil; import org.codehaus.plexus.PlexusContainer; @@ -220,10 +224,175 @@ assertEquals( 7785, ap.length() ); } - public void testWriteContention() + public void testWriteContentionSingleArtifact() throws Exception { + setSnapshots(); + + Set<Artifact> set = new HashSet<Artifact>(40); + + // prep. artifacts + for( int i=0; i<20; i++ ) + { + String si = ""+i; + + DefaultArtifact da = new DefaultArtifact( new ArtifactBasicMetadata("org.apache.maven:maven-core:2.0.9-20080805.215925-"+si) ); + da.setPomBlob( FileUtil.readRawData( getClass().getResourceAsStream( "/maven-core-2.0.9.pom" ) ) ); + File ab = File.createTempFile( "test-core-", "-bin" ); + FileUtil.writeRawData( ab, getClass().getResourceAsStream( "/maven-core-2.0.9.jar" ) ); + da.setFile( ab ); + set.add( da ); + } + + assertEquals( 20, set.size() ); + + long start = System.currentTimeMillis(); + // write 'em + writer.writeArtifact( set ); + + System.out.println("Took "+(System.currentTimeMillis()-start)+" millis to write "+set.size()+" artifacts"); + System.out.flush(); + + // check if the showed up in the repo + for( int i=0; i<20; i++ ) + { + String si = ""+i; + + String fn = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-core/2.0.9-SNAPSHOT/maven-core-2.0.9-20080805.215925-"+si+".jar"; + File af = new File( fn ); + assertTrue( fn+" does not exist", af.exists() ); + assertEquals( 159630, af.length() ); + + // is pom there also? + fn = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-core/2.0.9-SNAPSHOT/maven-core-2.0.9-20080805.215925-"+si+".pom"; + File ap = new File( fn ); + assertTrue( fn+" does not exist", ap.exists() ); + assertEquals( 7785, ap.length() ); + } + + // check GAV metadata has all versions + String mdFile = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-core/2.0.9-SNAPSHOT/"+repo.getMetadataName(); + byte [] mdBytes = FileUtil.readRawData( new File(mdFile) ); + Metadata md = MetadataBuilder.read( new ByteArrayInputStream(mdBytes) ); + + assertNotNull( md ); + assertNotNull( md.getVersioning() ); + assertNotNull( md.getVersioning().getVersions() ); + assertFalse( md.getVersioning().getVersions().isEmpty() ); + + List<String> versions = md.getVersioning().getVersions(); + System.out.println( versions.size()+" versions: " + versions ); + + assertEquals( 20, versions.size() ); + + for( int i=0; i<20;i++ ) + { + String v = "2.0.9-20080805.215925-"+i; + assertTrue( "Did not find "+v+" in GAV metadata "+mdFile+"\n"+new String(mdBytes), versions.contains( v ) ); + } + } + + + public void testWriteContentionMultipleArtifacts() + throws Exception + { + setSnapshots(); + + Set<Artifact> set = new HashSet<Artifact>(40); + + // prep. artifacts + for( int i=0; i<20; i++ ) + { + String si = ""+i; + + DefaultArtifact da = new DefaultArtifact( new ArtifactBasicMetadata("org.apache.maven:maven-core:2.0."+si+"-SNAPSHOT") ); + da.setPomBlob( FileUtil.readRawData( getClass().getResourceAsStream( "/maven-core-2.0.9.pom" ) ) ); + File ab = File.createTempFile( "test-core-", "-bin" ); + FileUtil.writeRawData( ab, getClass().getResourceAsStream( "/maven-core-2.0.9.jar" ) ); + da.setFile( ab ); + set.add( da ); + + da = new DefaultArtifact( new ArtifactBasicMetadata("org.apache.maven:maven-mercury:2.0."+si+"-SNAPSHOT") ); + da.setPomBlob( FileUtil.readRawData( getClass().getResourceAsStream( "/maven-core-2.0.9.pom" ) ) ); + ab = File.createTempFile( "test-mercury-", "-bin" ); + FileUtil.writeRawData( ab, getClass().getResourceAsStream( "/maven-core-2.0.9.jar" ) ); + da.setFile( ab ); + set.add( da ); + } + + assertEquals( 40, set.size() ); + + long start = System.currentTimeMillis(); + // write 'em + writer.writeArtifact( set ); + + System.out.println("Took "+(System.currentTimeMillis()-start)+" millis to write "+set.size()+" artifacts"); + System.out.flush(); + // check if the showed up in the repo + for( int i=0; i<20; i++ ) + { + String si = ""+i; + + String fn = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-core/2.0."+si+"-SNAPSHOT/maven-core-2.0."+si+"-SNAPSHOT.jar"; + File af = new File( targetDirectory, "/org/apache/maven/maven-core/2.0."+si+"-SNAPSHOT/maven-core-2.0."+si+"-SNAPSHOT.jar" ); + assertTrue( fn+" does not exist", af.exists() ); + assertEquals( 159630, af.length() ); + + fn = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-core/2.0."+si+"-SNAPSHOT/maven-core-2.0."+si+"-SNAPSHOT.pom"; + File ap = new File( targetDirectory, "/org/apache/maven/maven-core/2.0."+si+"-SNAPSHOT/maven-core-2.0."+si+"-SNAPSHOT.pom"); + assertTrue( fn+" does not exist", ap.exists() ); + assertEquals( 7785, ap.length() ); + + fn = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-mercury/2.0."+si+"-SNAPSHOT/maven-mercury-2.0."+i+"-SNAPSHOT.jar"; + af = new File( targetDirectory, "/org/apache/maven/maven-mercury/2.0."+si+"-SNAPSHOT/maven-mercury-2.0."+i+"-SNAPSHOT.jar"); + assertTrue( fn+" does not xist", af.exists() ); + assertEquals( 159630, af.length() ); + + fn = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-mercury/2.0."+si+"-SNAPSHOT/maven-mercury-2.0."+i+"-SNAPSHOT.pom"; + ap = new File( targetDirectory, "/org/apache/maven/maven-mercury/2.0."+si+"-SNAPSHOT/maven-mercury-2.0."+i+"-SNAPSHOT.pom"); + assertTrue( ap.exists() ); + assertEquals( 7785, ap.length() ); + } + + // check GA metadata has all versions + String mdFile = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-mercury/"+repo.getMetadataName(); + byte [] mdBytes = FileUtil.readRawData( new File(mdFile) ); + Metadata md = MetadataBuilder.read( new ByteArrayInputStream(mdBytes) ); + + assertNotNull( md ); + assertNotNull( md.getVersioning() ); + assertNotNull( md.getVersioning().getVersions() ); + assertFalse( md.getVersioning().getVersions().isEmpty() ); + + List<String> versions = md.getVersioning().getVersions(); + + assertEquals( 20, versions.size() ); + + for( int i=0; i<20;i++ ) + { + String v = "2.0."+i+"-SNAPSHOT"; + assertTrue( "Did not find "+v+" in GA metadata "+mdFile+"\n"+new String(mdBytes), versions.contains( v ) ); + } + + mdFile = targetDirectory.getAbsolutePath()+"/org/apache/maven/maven-core/"+repo.getMetadataName(); + mdBytes = FileUtil.readRawData( new File(mdFile) ); + md = MetadataBuilder.read( new ByteArrayInputStream(mdBytes) ); + + assertNotNull( md ); + assertNotNull( md.getVersioning() ); + assertNotNull( md.getVersioning().getVersions() ); + assertFalse( md.getVersioning().getVersions().isEmpty() ); + + versions = md.getVersioning().getVersions(); + + assertEquals( 20, versions.size() ); + + for( int i=0; i<20;i++ ) + { + String v = "2.0."+i+"-SNAPSHOT"; + assertTrue( "Did not find "+v+" in GA metadata "+mdFile+"\n"+new String(mdBytes), versions.contains( v ) ); + } } } Modified: maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/LocalRepositoryWriterM2Test.java URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/LocalRepositoryWriterM2Test.java?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/LocalRepositoryWriterM2Test.java (original) +++ maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/LocalRepositoryWriterM2Test.java Thu Sep 11 14:29:24 2008 @@ -25,6 +25,9 @@ public class LocalRepositoryWriterM2Test extends AbstractRepositoryWriterM2Test { + public static final String SYSTEM_PARAMETER_SKIP_LOCK_TESTS = "maven.mercury.tests.skip.lock"; + boolean skipLockTests = Boolean.parseBoolean( System.getProperty( SYSTEM_PARAMETER_SKIP_LOCK_TESTS, "true" ) ); + //------------------------------------------------------------------------------ @Override protected void setUp() @@ -34,6 +37,7 @@ targetDirectory = new File("./target/test-classes/tempRepo"); FileUtil.copy( new File("./target/test-classes/repo"), targetDirectory, true ); + FileUtil.delete( new File(targetDirectory, "org") ); mdProcessor = new MetadataProcessorMock(); @@ -69,5 +73,25 @@ throws MalformedURLException { } + //------------------------------------------------------------------------- + @Override + public void testWriteContentionMultipleArtifacts() + throws Exception + { + if( skipLockTests ) + System.out.println("Mutliple Artifacts contention test fails for local repo. Currently there is no way to synchronize those writes"); + else + super.testWriteContentionMultipleArtifacts(); + } + + @Override + public void testWriteContentionSingleArtifact() + throws Exception + { + if( skipLockTests ) + System.out.println("Single Artifacts contention test fails for remote repo. Currently there is no way to synchronize those writes"); + else + super.testWriteContentionSingleArtifact(); + } } Modified: maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/RemoteRepositoryWriterM2NexusTest.java URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/RemoteRepositoryWriterM2NexusTest.java?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/RemoteRepositoryWriterM2NexusTest.java (original) +++ maven/sandbox/trunk/mercury/mercury-it/src/test/java/org/apache/maven/mercury/repository/tests/RemoteRepositoryWriterM2NexusTest.java Thu Sep 11 14:29:24 2008 @@ -1,7 +1,6 @@ package org.apache.maven.mercury.repository.tests; import java.io.File; -import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; @@ -16,14 +15,6 @@ import org.apache.maven.mercury.transport.api.Credentials; import org.apache.maven.mercury.transport.api.Server; import org.apache.maven.mercury.util.FileUtil; -import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.component.repository.exception.ComponentLookupException; -import org.sonatype.appbooter.ForkedAppBooter; -import org.sonatype.appbooter.ctl.AppBooterServiceException; -import org.sonatype.nexus.client.NexusClient; -import org.sonatype.nexus.client.NexusClientException; -import org.sonatype.nexus.client.NexusConnectionException; -import org.sonatype.nexus.client.rest.NexusRestClient; /** * @@ -114,5 +105,20 @@ super.tearDown(); } //------------------------------------------------------------------------- + @Override + public void testWriteContentionMultipleArtifacts() + throws Exception + { + System.out.println("Mutliple Artifacts contention test fails for remote repo. Currently there is no way to synchronize those writes"); + } + + @Override + public void testWriteContentionSingleArtifact() + throws Exception + { + System.out.println("Single Artifacts contention test fails for remote repo. Currently there is no way to synchronize those writes"); + } + + //------------------------------------------------------------------------- //------------------------------------------------------------------------- } Modified: maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/LocalRepositoryWriterM2.java URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/LocalRepositoryWriterM2.java?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/LocalRepositoryWriterM2.java (original) +++ maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/LocalRepositoryWriterM2.java Thu Sep 11 14:29:24 2008 @@ -1,16 +1,23 @@ package org.apache.maven.mercury.repository.local.m2; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; +import java.nio.channels.FileLock; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import org.apache.maven.mercury.artifact.Artifact; import org.apache.maven.mercury.artifact.Quality; import org.apache.maven.mercury.artifact.version.DefaultArtifactVersion; +import org.apache.maven.mercury.crypto.api.StreamObserverException; import org.apache.maven.mercury.crypto.api.StreamVerifierFactory; import org.apache.maven.mercury.repository.api.AbstractRepository; import org.apache.maven.mercury.repository.api.LocalRepository; @@ -20,26 +27,42 @@ import org.apache.maven.mercury.repository.metadata.AddVersionOperation; import org.apache.maven.mercury.repository.metadata.Metadata; import org.apache.maven.mercury.repository.metadata.MetadataBuilder; +import org.apache.maven.mercury.repository.metadata.MetadataException; import org.apache.maven.mercury.repository.metadata.MetadataOperation; import org.apache.maven.mercury.repository.metadata.SetSnapshotOperation; import org.apache.maven.mercury.repository.metadata.Snapshot; import org.apache.maven.mercury.repository.metadata.SnapshotOperand; import org.apache.maven.mercury.repository.metadata.StringOperand; import org.apache.maven.mercury.transport.api.Server; +import org.apache.maven.mercury.util.FileLockBundle; import org.apache.maven.mercury.util.FileUtil; import org.codehaus.plexus.i18n.DefaultLanguage; import org.codehaus.plexus.i18n.Language; public class LocalRepositoryWriterM2 +extends Thread implements RepositoryWriter { + public static final String SYSTEM_PROPERTY_PARALLEL_WORKERS = "mercury.local.repo.workers"; + public static final int PARALLEL_WORKERS = Integer.parseInt( System.getProperty( SYSTEM_PROPERTY_PARALLEL_WORKERS, "4" ) ); + + public static final long SLEEP_FOR_WORKERS_TICK = 20l; + + public static final String SYSTEM_PROPERTY_SLEEP_FOR_LOCK = "mercury.local.lock.wait.millis"; + public static final long SLEEP_FOR_LOCK = Long.parseLong( System.getProperty( SYSTEM_PROPERTY_SLEEP_FOR_LOCK, "5000" ) ); + + public static final long SLEEP_FOR_LOCK_TICK = 5l; + private static final org.slf4j.Logger _log = org.slf4j.LoggerFactory.getLogger( LocalRepositoryWriterM2.class ); private static final Language _lang = new DefaultLanguage( LocalRepositoryReaderM2.class ); //--------------------------------------------------------------------------------------------------------------- private static final String [] _protocols = new String [] { "file" }; - LocalRepository _repo; - File _repoDir; + private final LocalRepository _repo; + private final File _repoDir; + private final ArtifactQueue _aq; + + private static final ArifactWriteData LAST_ARTIFACT = new ArifactWriteData( null, null ); //--------------------------------------------------------------------------------------------------------------- public LocalRepositoryWriterM2( LocalRepository repo ) { @@ -54,6 +77,14 @@ throw new IllegalArgumentException("localRepo directory \""+_repoDir.getAbsolutePath()+"\" should exist"); _repo = repo; + _aq = null; + } + //--------------------------------------------------------------------------------------------------------------- + private LocalRepositoryWriterM2( LocalRepository repo, File repoDir, ArtifactQueue aq ) + { + _repo = repo; + _repoDir = repoDir; + _aq = aq; } //--------------------------------------------------------------------------------------------------------------- public Repository getRepository() @@ -81,21 +112,74 @@ if( artifacts == null || artifacts.size() < 1 ) return; - Set<StreamVerifierFactory> vFacs = null; - Server server = _repo.getServer(); - if( server != null && server.hasWriterStreamVerifierFactories() ) - vFacs = server.getWriterStreamVerifierFactories(); + int nWorkers = PARALLEL_WORKERS; + if( artifacts.size() < nWorkers ) + nWorkers = artifacts.size(); - if( vFacs == null ) // let it be empty, but not null - vFacs = new HashSet<StreamVerifierFactory>(1); + ArtifactQueue aq = new ArtifactQueue(); + LocalRepositoryWriterM2 [] workers = new LocalRepositoryWriterM2[ nWorkers ]; + for( int i=0; i<nWorkers; i++ ) + { + workers[ i ] = new LocalRepositoryWriterM2( _repo, _repoDir, aq ); + workers[ i ].start(); + } + for( Artifact artifact : artifacts ) { - writeArtifact( artifact, vFacs ); + Set<StreamVerifierFactory> vFacs = null; + Server server = _repo.getServer(); + if( server != null && server.hasWriterStreamVerifierFactories() ) + vFacs = server.getWriterStreamVerifierFactories(); + + if( vFacs == null ) // let it be empty, but not null + vFacs = new HashSet<StreamVerifierFactory>(1); + + aq.addArtifact( new ArifactWriteData( artifact, vFacs ) ); + } + aq.addArtifact( LAST_ARTIFACT ); + + boolean alive = true; + while( alive ) + { + alive = false; + for( int i=0; i<nWorkers; i++ ) + if( workers[ i ].isAlive() ) + { + alive = true; + try { sleep( SLEEP_FOR_WORKERS_TICK ); } catch( InterruptedException ie ) {} + } } } //--------------------------------------------------------------------------------------------------------------- - public void writeArtifact( Artifact artifact, Set<StreamVerifierFactory> vFacs ) + /* (non-Javadoc) + * @see java.lang.Thread#run() + */ + @Override + public void run() + { + try + { + for(;;) + { + ArifactWriteData awd = _aq.getArtifact(); + + if( awd == null || awd.artifact == null ) + break; + + writeArtifact( awd.artifact, awd.vFacs ); + } + } + catch (InterruptedException e) + { + } + catch( RepositoryException e ) + { + throw new RuntimeException(e); + } + } + //--------------------------------------------------------------------------------------------------------------- + public void writeArtifact( final Artifact artifact, final Set<StreamVerifierFactory> vFacs ) throws RepositoryException { if( artifact == null ) @@ -130,10 +214,15 @@ boolean isSnapshot = aq.equals( Quality.SNAPSHOT_QUALITY ) || aq.equals( Quality.SNAPSHOT_TS_QUALITY ); String relGroupPath = artifact.getGroupId().replace( '.', '/' )+"/"+artifact.getArtifactId(); - String relVersionPath = relGroupPath + '/' + (isSnapshot ? (dav.getBase()+'-'+Artifact.SNAPSHOT_VERSION) : artifact.getVersion() ); + String versionDirName = isSnapshot ? (dav.getBase()+'-'+Artifact.SNAPSHOT_VERSION) : artifact.getVersion(); + String relVersionPath = relGroupPath + '/' + versionDirName; + + String lockDir = null; + FileLockBundle fLock = null; try { + if( isPom ) { if( in == null && !hasPomBlob ) @@ -146,69 +235,37 @@ if( hasPomBlob ) pomBlob = pomBlobBytes; } - } // create folders - File gav = new File( _repoDir, relVersionPath ); + lockDir = _repoDir.getAbsolutePath()+'/'+relGroupPath; + + File gav = new File( lockDir ); gav.mkdirs(); +// haveLock = FileUtil.lockDir( lockDir, SLEEP_FOR_LOCK, SLEEP_FOR_LOCK_TICK ); +// if( !haveLock ) +// throw new RepositoryException( _lang.getMessage( "cannot.lock.gav", lockDir, ""+SLEEP_FOR_LOCK ) ); + fLock = FileUtil.lockDir( lockDir, SLEEP_FOR_LOCK, SLEEP_FOR_LOCK_TICK ); + if( fLock == null ) + throw new RepositoryException( _lang.getMessage( "cannot.lock.gav", lockDir, ""+SLEEP_FOR_LOCK ) ); + String fName = _repoDir.getAbsolutePath()+'/'+relVersionPath+'/'+artifact.getBaseName()+'.'+artifact.getType(); - if( !isPom ) - { - // first - take care of the binary + if( !isPom ) // first - take care of the binary FileUtil.writeAndSign( fName, in, vFacs ); - - // if classier - nothing else to do :) - if( artifact.hasClassifier() ) - return; - - // GA metadata - File mdFile = new File( _repoDir, relGroupPath+'/'+_repo.getMetadataName() ); - Metadata localMd = null; - - if( mdFile.exists() ) - localMd = MetadataBuilder.read( new FileInputStream(mdFile) ); - else - { - localMd = new Metadata(); - localMd.setGroupId( artifact.getGroupId() ); - localMd.setArtifactId( artifact.getArtifactId() ); - } - - MetadataOperation mdOp = null; - - if( isSnapshot ) - { - Snapshot sn = MetadataBuilder.createSnapshot( artifact.getVersion() ); - sn.setLocalCopy( true ); - mdOp = new SetSnapshotOperation( new SnapshotOperand(sn) ); - } - else - mdOp = new AddVersionOperation( new StringOperand(artifact.getVersion()) ); - - byte [] resBytes = MetadataBuilder.changeMetadata( localMd, mdOp ); - - FileUtil.writeAndSign( mdFile.getAbsolutePath(), resBytes, vFacs ); - // now - GAV metadata - mdFile = new File( _repoDir, relVersionPath+'/'+_repo.getMetadataName() ); - localMd = null; - - if( mdFile.exists() ) - localMd = MetadataBuilder.read( new FileInputStream(mdFile) ); - else - { - localMd = new Metadata(); - localMd.setGroupId( artifact.getGroupId() ); - localMd.setArtifactId( artifact.getArtifactId() ); - localMd.setVersion( artifact.getVersion() ); - } - - resBytes = MetadataBuilder.changeMetadata( localMd, mdOp ); - FileUtil.writeAndSign( mdFile.getAbsolutePath(), resBytes, vFacs ); - } + // GA metadata + File mdFile = new File( _repoDir, relGroupPath+'/'+_repo.getMetadataName() ); + updateGAMetadata( mdFile, artifact, versionDirName, aq, vFacs ); + + // now - GAV metadata + mdFile = new File( _repoDir, relVersionPath+'/'+_repo.getMetadataName() ); + updateGAVMetadata( mdFile, artifact, aq, vFacs ); + + // if classier - nothing else to do :) + if( artifact.hasClassifier() ) + return; if( hasPomBlob ) { @@ -222,8 +279,137 @@ { throw new RepositoryException( e ); } + finally + { + if( fLock != null ) + fLock.release(); + } + + } + //--------------------------------------------------------------------------------------------------------------- + private void updateGAMetadata( final File mdFile + , final Artifact artifact + , final String version + , final Quality aq + , final Set<StreamVerifierFactory> vFacs + ) + throws MetadataException, IOException, StreamObserverException + { + Metadata md = null; + + if( mdFile.exists() ) + { + try + { + byte [] mdBytes = FileUtil.readRawData( mdFile ); + + if( mdBytes == null ) + throw new MetadataException( _lang.getMessage( "file.is.empty", mdFile.getAbsolutePath() )); + + md = MetadataBuilder.read( new ByteArrayInputStream(mdBytes) ); + } + catch( MetadataException e ) + { + throw e; + } + } + else + { + md = new Metadata(); + md.setGroupId( artifact.getGroupId() ); + md.setArtifactId( artifact.getArtifactId() ); + } + + MetadataOperation mdOp = new AddVersionOperation( new StringOperand( version ) ); + + byte [] resBytes = MetadataBuilder.changeMetadata( md, mdOp ); + + FileUtil.writeAndSign( mdFile.getAbsolutePath(), resBytes, vFacs ); + } + //--------------------------------------------------------------------------------------------------------------- + private void updateGAVMetadata( final File mdFile + , final Artifact artifact + , final Quality aq + , final Set<StreamVerifierFactory> vFacs + ) + throws MetadataException, IOException, StreamObserverException + { + Metadata md = null; + + if( mdFile.exists() ) + { + byte [] mdBytes = FileUtil.readRawData( mdFile ); + md = MetadataBuilder.read( new ByteArrayInputStream(mdBytes) ); + } + else + { + md = new Metadata(); + md.setGroupId( artifact.getGroupId() ); + md.setArtifactId( artifact.getArtifactId() ); + md.setVersion( artifact.getVersion() ); + } + List<MetadataOperation> mdOps = new ArrayList<MetadataOperation>(2); + + if( aq.equals( Quality.SNAPSHOT_TS_QUALITY ) ) + { + Snapshot sn = MetadataBuilder.createSnapshot( artifact.getVersion() ); + sn.setLocalCopy( true ); + mdOps.add( new SetSnapshotOperation( new SnapshotOperand(sn) ) ); + } + mdOps.add( new AddVersionOperation( new StringOperand(artifact.getVersion()) ) ); + +System.out.println("added "+artifact.getVersion()); +System.out.flush(); + byte [] resBytes = MetadataBuilder.changeMetadata( md, mdOps ); + FileUtil.writeAndSign( mdFile.getAbsolutePath(), resBytes, vFacs ); } //--------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------- } +//================================================================================================================= +class ArifactWriteData +{ + Artifact artifact; + Set<StreamVerifierFactory> vFacs; + + public ArifactWriteData(Artifact artifact, Set<StreamVerifierFactory> vFacs) + { + this.artifact = artifact; + this.vFacs = vFacs; + } +} +//================================================================================================================= +class ArtifactQueue +{ + LinkedList<ArifactWriteData> queue = new LinkedList<ArifactWriteData>(); + boolean empty = false; + + public synchronized void addArtifact( ArifactWriteData awd ) + { + queue.addLast( awd ); + empty = false; + notify(); + } + + public synchronized ArifactWriteData getArtifact() + throws InterruptedException + { + if( empty ) + return null; + + while( queue.isEmpty() ) + wait(); + + ArifactWriteData res = queue.removeFirst(); + + if( res.artifact == null ) + { + empty = true; + return null; + } + + return res; + } +} +//================================================================================================================= Modified: maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/Messages.properties URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/Messages.properties?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/Messages.properties (original) +++ maven/sandbox/trunk/mercury/mercury-repo/mercury-repo-local-m2/src/main/java/org/apache/maven/mercury/repository/local/m2/Messages.properties Thu Sep 11 14:29:24 2008 @@ -10,4 +10,6 @@ pom.artifact.no.stream=Cannot find either pom blob, input stream or file, associated with artifact {0} no.signature.file=Verifier for {0} is mandatory, but file {1} does not exist signature.failed=Signature "{0}": verification failed for file {1} -cannot.read.signature.file=Cannot read signature file {0}, error: {1} \ No newline at end of file +cannot.read.signature.file=Cannot read signature file {0}, error: {1} +cannot.lock.gav=Cannot lock GAV folder {0} in {1} millis +file.is.empty=File {0} exists, but is empty. Data corruption somewhere - please repair metadata. \ No newline at end of file Added: maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileLockBundle.java URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileLockBundle.java?rev=694493&view=auto ============================================================================== --- maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileLockBundle.java (added) +++ maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileLockBundle.java Thu Sep 11 14:29:24 2008 @@ -0,0 +1,66 @@ +package org.apache.maven.mercury.util; + +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; + +/** + * + * + * @author Oleg Gusakov + * @version $Id$ + * + */ +public class FileLockBundle +{ + String dir; + FileChannel channel; + FileLock lock; + + boolean fileLock = false; + + /** + * @param dir + * @param channel + * @param lock + */ + public FileLockBundle( + String dir, + FileChannel channel, + FileLock lock + ) + { + this.dir = dir; + this.channel = channel; + this.lock = lock; + } + + /** + * @param dir + * @param channel + * @param lock + */ + public FileLockBundle( String dir ) + { + this.dir = dir; + this.fileLock = true; + } + + public void release() + { + if( lock == null ) + { + if( fileLock ) + FileUtil.unlockDir( dir ); + + return; + } + + try + { + lock.release(); + channel.close(); + } + catch( IOException any ){} + } +} Modified: maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileUtil.java URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileUtil.java?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileUtil.java (original) +++ maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/FileUtil.java Thu Sep 11 14:29:24 2008 @@ -56,6 +56,8 @@ //--------------------------------------------------------------------------------------------------------------- private static final org.slf4j.Logger _log = org.slf4j.LoggerFactory.getLogger( FileUtil.class ); private static final Language _lang = new DefaultLanguage( FileUtil.class ); + + private static final OverlappingFileLockException FILE_LOCKED = new OverlappingFileLockException(); //--------------------------------------------------------------------------------------------------------------- public static void delete( File f ) { @@ -133,8 +135,18 @@ { fis = new FileInputStream( file ); int len = (int)file.length(); + if( len == 0 ) + { + _log.info( _lang.getMessage( "reading.empty.file", file.getAbsolutePath() ) ); + return null; + } + byte [] pom = new byte [ len ]; - fis.read( pom ); + while( fis.available() < 1 ) + try { Thread.sleep( 8L ); } catch( InterruptedException e ){} + + fis.read( pom, 0, len ); + return pom; } catch( IOException e ) @@ -281,12 +293,18 @@ if( file.exists() ) file.delete(); + File parentDir = file.getParentFile(); + + if( !parentDir.exists() ) + parentDir.mkdirs(); + FileOutputStream fos = null; try { fos = new FileOutputStream( file ); fos.write( bytes ); + fos.flush(); } catch( IOException e ) { @@ -320,6 +338,7 @@ try { File f = new File( fName ); + f.getParentFile().mkdirs(); fout = new FileOutputStream( f ); @@ -332,6 +351,10 @@ fout.write( buf, 0, n ); } + fout.flush(); + fout.close(); + fout = null; + for( StreamVerifier sv : vSet ) { String sig = sv.getSignature(); @@ -685,7 +708,7 @@ } //--------------------------------------------------------------------------------------------------------------- /** - * try to acquire lock on specfied directory for <code>millis<code> millis + * try to acquire lock on specified directory for <code>millis<code> milliseconds * * @param dir directory to lock * @param millis how long to wait for the lock before surrendering @@ -694,46 +717,145 @@ * @return obtained FileLock or null * @throws IOException if there were problems obtaining the lock */ - public static boolean lockDir( String dir, long millis, long sleepFor ) + public static FileLockBundle lockDir( String dir, long millis, long sleepFor ) throws IOException { File df = new File(dir); + + boolean exists = df.exists(); + + for( int i=0; i<10 && !exists; i++ ) + { + try{ Thread.sleep( 1l );} catch( InterruptedException e ){} + df.mkdirs(); + exists = df.exists(); + _log.info( _lang.getMessage( "had.to.create.directory", dir, exists+"" ) ); + } + + if( !exists ) + throw new IOException( _lang.getMessage( "cannot.create.directory", dir ) ); + if( !df.isDirectory() ) - throw new IOException( _lang.getMessage( "file.is.not.directory", dir ) ); + throw new IOException( _lang.getMessage( "file.is.not.directory", dir, df.exists()+"", df.isDirectory()+"", df.isFile()+"" ) ); File lock = new File(dir,LOCK_FILE); long start = System.currentTimeMillis(); + + byte [] lockId = (""+System.nanoTime()+""+Math.random()).getBytes(); + int lockIdLen = lockId.length; for(;;) try { if( lock.exists() ) throw new OverlappingFileLockException(); + FileOutputStream fos = new FileOutputStream( lock ); - fos.write( 32 ); + fos.write( lockId, 0, lockIdLen ); fos.flush(); fos.close(); + + byte [] lockBytes = readRawData( lock ); + int lockBytesLen = lockBytes.length; + + if( lockBytesLen != lockIdLen ) + throw new OverlappingFileLockException(); + + for( int i=0; i<lockIdLen; i++ ) + if( lockBytes[i] != lockId[i] ) + throw new OverlappingFileLockException(); + + lock.deleteOnExit(); - return true; + return new FileLockBundle(dir); } catch( OverlappingFileLockException le ) { try { Thread.sleep( sleepFor ); } catch( InterruptedException e ){} if( System.currentTimeMillis() - start > millis ) - return false; + return null; } } //--------------------------------------------------------------------------------------------------------------- - public static void unlockDir( String dir ) + /** + * try to acquire lock on specified directory for <code>millis<code> milliseconds + * + * @param dir directory to lock + * @param millis how long to wait for the lock before surrendering + * @param sleepFor how long to sleep between attempts + * + * @return obtained FileLock or null + * @throws IOException if there were problems obtaining the lock + */ + public static FileLockBundle lockDirNio( String dir, long millis, long sleepFor ) throws IOException { File df = new File(dir); + + boolean exists = df.exists(); + + for( int i=0; i<10 && !exists; i++ ) + { + try{ Thread.sleep( 1l );} catch( InterruptedException e ){} + df.mkdirs(); + exists = df.exists(); + _log.info( _lang.getMessage( "had.to.create.directory", dir, exists+"" ) ); + } + + if( !exists ) + throw new IOException( _lang.getMessage( "cannot.create.directory", dir ) ); + if( !df.isDirectory() ) - throw new IOException( _lang.getMessage( "file.is.not.directory", dir ) ); + throw new IOException( _lang.getMessage( "file.is.not.directory", dir, df.exists()+"", df.isDirectory()+"", df.isFile()+"" ) ); - File lock = new File(dir,LOCK_FILE); - if( lock.exists() ) - lock.delete(); + File lockFile = new File(dir,LOCK_FILE); + if( !lockFile.exists() ) + writeRawData( lockFile, "lock" ); + lockFile.deleteOnExit(); + + FileChannel ch = new RandomAccessFile( lockFile, "rw" ).getChannel(); + FileLock lock = null; +System.out.println("locking channel "+lockFile.getAbsolutePath()+", channel isOpen()="+ch.isOpen() ); +System.out.flush(); + + long start = System.currentTimeMillis(); + + for(;;) + try + { + lock = ch.tryLock( 0L, 4L, false ); + + if( lock == null ) + throw FILE_LOCKED; + + return new FileLockBundle( dir, ch, lock ); + } + catch( OverlappingFileLockException oe ) + { +System.out.println("channel "+lockFile.getAbsolutePath()+" locked, waiting" ); +System.out.flush(); + try { Thread.sleep( sleepFor ); } catch( InterruptedException e ){} + if( System.currentTimeMillis() - start > millis ) + return null; + } + } + //--------------------------------------------------------------------------------------------------------------- + public static void unlockDir( String dir ) + { + try + { + File df = new File(dir); + if( !df.isDirectory() ) + throw new IOException( _lang.getMessage( "file.is.not.directory", dir ) ); + + File lock = new File(dir,LOCK_FILE); + if( lock.exists() ) + lock.delete(); + } + catch( IOException e ) + { + _log.error( e.getMessage() ); + } } //--------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------- Modified: maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/Messages.properties URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/Messages.properties?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/Messages.properties (original) +++ maven/sandbox/trunk/mercury/mercury-util/src/main/java/org/apache/maven/mercury/util/Messages.properties Thu Sep 11 14:29:24 2008 @@ -16,5 +16,8 @@ file.is.directory=File "{0}" is a folder and no recursive option is specified. Skipping .. no.mandatory.signature=Mandatory signature {1} does not exist for file {0} file.failed.verification=File {0} failed following verifications: {1} - -file.is.not.directory=Specified file {0} is not a directory +cannot.create.directory=Cannot create directory {0} +file.is.not.directory=Specified file {0} is not a directory: exists={1}, isDir={2}, isFile={3} +reading.empty.file=File to read {0} has zero length +had.to.create.directory=Directory {0} did not exist. Created: {1} +null.file=Null supplied instead on File instance Modified: maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/FileUtilTest.java URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/FileUtilTest.java?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/FileUtilTest.java (original) +++ maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/FileUtilTest.java Thu Sep 11 14:29:24 2008 @@ -2,7 +2,10 @@ import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import java.util.HashSet; import junit.framework.TestCase; @@ -24,6 +27,9 @@ public class FileUtilTest extends TestCase { + public static final String SYSTEM_PARAMETER_SKIP_NIO_TESTS = "maven.mercury.tests.skip.nio"; + boolean skipNioTests = Boolean.parseBoolean( System.getProperty( SYSTEM_PARAMETER_SKIP_NIO_TESTS, "true" ) ); + private static final String publicKeyFile = "/pgp/pubring.gpg"; private static final String secretKeyFile = "/pgp/secring.gpg"; private static final String keyId = "0EDB5D91141BC4F2"; @@ -147,34 +153,48 @@ setPgp( false ); FileUtil.verify( b, vFacs, false, true ); } + + //---------------------------------------------------------------------------------------- + private static void say( String msg ) + { + System.out.println(msg); + System.out.flush(); + } //---------------------------------------------------------------------------------------- public void testLock() throws Exception { + Ok th1ok = new Ok(); + Ok th2ok = new Ok(); + class TestThread1 extends Thread { - boolean lock; + FileLockBundle lock; String dir; + Ok ok; - public TestThread1( String dir ) + public TestThread1( String dir, Ok ok ) { this.dir = dir; + this.ok = ok; } @Override public void run() { try { - lock = FileUtil.lockDir( dir, 1000L, 100L ); - assertTrue( lock ); - System.out.println("Thread1: lock "+dir+" obtained"); + lock = FileUtil.lockDir( dir, 10L, 10L ); + assertNotNull( lock ); + say("Thread1: lock "+dir+" obtained"); try { sleep( 2000L ); } catch( InterruptedException e ) {} - System.out.println("Thread1: slept for 2s"); + say("Thread1: slept for 2s"); + + lock.release(); + say("Thread1: lock "+dir+" released"); - FileUtil.unlockDir( dir ); - System.out.println("Thread1: lock "+dir+" released"); + ok.ok(); } catch( Exception e ) { @@ -187,27 +207,32 @@ class TestThread2 extends Thread { - boolean lock; + FileLockBundle lock; String dir; + Ok ok; - public TestThread2( String dir ) + public TestThread2( String dir, Ok ok ) { this.dir = dir; + this.ok = ok; } @Override public void run() { try { + sleep(10l); lock = FileUtil.lockDir( dir, 10L, 10L ); - assertFalse( lock ); - System.out.println("Thread2: resource "+dir+" locked"); + assertNull( lock ); + say("Thread2: resource "+dir+" locked"); lock = FileUtil.lockDir( dir, 5000L, 100L ); assertNotNull( lock ); - FileUtil.unlockDir( dir ); - System.out.println("Thread2: lock "+dir+" released"); + lock.release(); + say("Thread2: lock "+dir+" released"); + + ok.ok(); } catch( Exception e ) { @@ -224,8 +249,8 @@ dir.mkdir(); dir.deleteOnExit(); - TestThread1 th1 = new TestThread1( dirName ); - TestThread2 th2 = new TestThread2( dirName ); + TestThread1 th1 = new TestThread1( dirName, th1ok ); + TestThread2 th2 = new TestThread2( dirName, th2ok ); th1.start(); th2.start(); @@ -236,7 +261,145 @@ else break; - System.out.println("Multi-threaded test finished successfully"); + + assertTrue( th1ok.isOk() ); + + assertTrue( th2ok.isOk() ); + + say("Multi-threaded test finished successfully"); } //---------------------------------------------------------------------------------------- + public void testLockNio() + throws Exception + { + Ok th1ok = new Ok(); + Ok th2ok = new Ok(); + + class TestThread1 + extends Thread + { + FileLockBundle lock; + String dir; + Ok ok; + + public TestThread1( String dir, Ok ok ) + { + this.dir = dir; + this.ok = ok; + } + @Override + public void run() + { + try + { + lock = FileUtil.lockDirNio( dir, 10L, 10L ); + say("NioThread1: got lock "+lock+" on "+dir+" obtained"); + + assertNotNull( lock ); + say("NioThread1: lock "+dir+" obtained"); + + try { sleep( 2000L ); } catch( InterruptedException e ) {} + say("NioThread1: slept for 2s"); + + lock.release(); + say("NioThread1: lock "+dir+" released"); + + ok.ok(); + } + catch( Exception e ) + { + fail( e.getMessage() ); + } + } + + } + + class TestThread2 + extends Thread + { + FileLockBundle lock; + String dir; + Ok ok; + + public TestThread2( String dir, Ok ok ) + { + this.dir = dir; + this.ok = ok; + } + @Override + public void run() + { + try + { + sleep(10l); + lock = FileUtil.lockDirNio( dir, 10L, 10L ); + say("NioThread2: got lock "+lock+" on "+dir+" obtained"); + + assertNull( lock ); + + System.out.println("NioThread2: resource "+dir+" busy"); + System.out.flush(); + + lock = FileUtil.lockDirNio( dir, 5000L, 100L ); + assertNotNull( lock ); + + say("NioThread2: lock "+dir+" obtained"); + + lock.release(); + say("NioThread2: lock "+dir+" released"); + + ok.ok(); + } + catch( Exception e ) + { + fail( e.getMessage() ); + } + } + + } + + File dir = File.createTempFile( "test-", "-dir" ); + String dirName = dir.getAbsolutePath(); + dir.delete(); + dir = new File( dirName ); + dir.mkdir(); + dir.deleteOnExit(); + + TestThread1 th1 = new TestThread1( dirName, th1ok ); + TestThread2 th2 = new TestThread2( dirName, th2ok ); + + th1.start(); + th2.start(); + + for(;;) + if( th1.isAlive() || th2.isAlive() ) + Thread.sleep( 1000L ); + else + break; + +if(skipNioTests) + return; + + assertTrue( th1ok.isOk() ); + + assertTrue( th2ok.isOk() ); + + say("Multi-threaded NIO test finished successfully"); + } +} + + +class Ok +{ + boolean ok = false; + + public void ok() + { + ok = true; + } + + public boolean isOk() + { + return ok; + } } Modified: maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/NioTest.java URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/NioTest.java?rev=694493&r1=694492&r2=694493&view=diff ============================================================================== --- maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/NioTest.java (original) +++ maven/sandbox/trunk/mercury/mercury-util/src/test/java/org/apache/maven/mercury/util/NioTest.java Thu Sep 11 14:29:24 2008 @@ -57,6 +57,7 @@ file.delete(); +if(false) assertTrue( "java.nio does not work !!", ok ); } catch (Exception e)