You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2021/05/02 09:04:12 UTC

[maven-resolver] 01/01: [MRESOLVER-153] Make TrackingFileManager use named locks as well

This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch MRESOLVER-176-tracking-file-manager
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git

commit 1decd98cf6e1a6892a3e4757167c8c24f7de7b4f
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Fri Apr 30 10:46:10 2021 +0200

    [MRESOLVER-153] Make TrackingFileManager use named locks as well
    
    Depends on components introduced in https://github.com/apache/maven-resolver/pull/101/files
    
    Use selected NamedLocksFactory in DefaultTrackingFileManager as well
    to protected property file reads/writes.
---
 .../internal/impl/DefaultTrackingFileManager.java  | 211 ++++++++++++++-------
 .../internal/impl/DefaultArtifactResolverTest.java |   5 +-
 .../impl/DefaultTrackingFileManagerTest.java       |  14 +-
 .../impl/DefaultUpdateCheckManagerTest.java        |   4 +-
 .../impl/EnhancedLocalRepositoryManagerTest.java   |   4 +-
 5 files changed, 152 insertions(+), 86 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java
index e6e2e65..c4d8a72 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java
@@ -27,11 +27,18 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Properties;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import org.eclipse.aether.internal.impl.synccontext.NamedLockFactorySelector;
+import org.eclipse.aether.named.NamedLock;
+import org.eclipse.aether.named.NamedLockFactory;
+import org.eclipse.aether.spi.locator.Service;
+import org.eclipse.aether.spi.locator.ServiceLocator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,100 +48,170 @@ import org.slf4j.LoggerFactory;
 @Singleton
 @Named
 public final class DefaultTrackingFileManager
-    implements TrackingFileManager
+    implements TrackingFileManager, Service
 {
     private static final Logger LOGGER = LoggerFactory.getLogger( DefaultTrackingFileManager.class );
 
+    private static final String LOCK_PREFIX = "tracking:";
+
+    private NamedLockFactory namedLockFactory;
+
+    public DefaultTrackingFileManager()
+    {
+        // ctor for ServiceLocator
+    }
+
+    @Inject
+    public DefaultTrackingFileManager( final NamedLockFactorySelector selector )
+    {
+        this.namedLockFactory = selector.getSelectedNamedLockFactory();
+    }
+
+    @Override
+    public void initService( final ServiceLocator locator )
+    {
+        NamedLockFactorySelector select = Objects.requireNonNull(
+            locator.getService( NamedLockFactorySelector.class ) );
+        this.namedLockFactory = select.getSelectedNamedLockFactory();
+    }
+
+    private String getFileKey( final File file )
+    {
+        return LOCK_PREFIX + file.getAbsolutePath();
+    }
+
     @Override
     public Properties read( File file )
     {
-        FileInputStream stream = null;
-        try
+        try ( NamedLock lock = namedLockFactory.getLock( getFileKey( file ) ) )
         {
-            if ( !file.exists() )
+            if ( lock.lockShared( NamedLockFactorySelector.TIME, NamedLockFactorySelector.TIME_UNIT ) )
             {
-                return null;
+                try
+                {
+                    FileInputStream stream = null;
+                    try
+                    {
+                        if ( !file.exists() )
+                        {
+                            return null;
+                        }
+
+                        stream = new FileInputStream( file );
+
+                        Properties props = new Properties();
+                        props.load( stream );
+
+                        return props;
+                    }
+                    catch ( IOException e )
+                    {
+                        LOGGER.warn( "Failed to read tracking file {}", file, e );
+                    }
+                    finally
+                    {
+                        close( stream, file );
+                    }
+
+                    return null;
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+            else
+            {
+                throw new IllegalStateException( "Could not acquire read lock for: " + file );
             }
-
-            stream = new FileInputStream( file );
-
-            Properties props = new Properties();
-            props.load( stream );
-
-            return props;
-        }
-        catch ( IOException e )
-        {
-            LOGGER.warn( "Failed to read tracking file {}", file, e );
         }
-        finally
+        catch ( InterruptedException e )
         {
-            close( stream, file );
+            throw new IllegalStateException( e );
         }
-
-        return null;
     }
 
     @Override
     public Properties update( File file, Map<String, String> updates )
     {
-        Properties props = new Properties();
-
-        File directory = file.getParentFile();
-        if ( !directory.mkdirs() && !directory.exists() )
-        {
-            LOGGER.warn( "Failed to create parent directories for tracking file {}", file );
-            return props;
-        }
-
-        RandomAccessFile raf = null;
-        try
+        try ( NamedLock lock = namedLockFactory.getLock( getFileKey( file ) ) )
         {
-            raf = new RandomAccessFile( file, "rw" );
-
-            if ( file.canRead() )
+            if ( lock.lockExclusively( NamedLockFactorySelector.TIME, NamedLockFactorySelector.TIME_UNIT ) )
             {
-                byte[] buffer = new byte[(int) raf.length()];
-
-                raf.readFully( buffer );
-
-                ByteArrayInputStream stream = new ByteArrayInputStream( buffer );
-
-                props.load( stream );
-            }
-
-            for ( Map.Entry<String, String> update : updates.entrySet() )
-            {
-                if ( update.getValue() == null )
+                try
                 {
-                    props.remove( update.getKey() );
+                    Properties props = new Properties();
+
+                    File directory = file.getParentFile();
+                    if ( !directory.mkdirs() && !directory.exists() )
+                    {
+                        LOGGER.warn( "Failed to create parent directories for tracking file {}", file );
+                        return props;
+                    }
+
+                    RandomAccessFile raf = null;
+                    try
+                    {
+                        raf = new RandomAccessFile( file, "rw" );
+
+                        if ( file.canRead() )
+                        {
+                            byte[] buffer = new byte[(int) raf.length()];
+
+                            raf.readFully( buffer );
+
+                            ByteArrayInputStream stream = new ByteArrayInputStream( buffer );
+
+                            props.load( stream );
+                        }
+
+                        for ( Map.Entry<String, String> update : updates.entrySet() )
+                        {
+                            if ( update.getValue() == null )
+                            {
+                                props.remove( update.getKey() );
+                            }
+                            else
+                            {
+                                props.setProperty( update.getKey(), update.getValue() );
+                            }
+                        }
+
+                        ByteArrayOutputStream stream = new ByteArrayOutputStream( 1024 * 2 );
+
+                        LOGGER.debug( "Writing tracking file {}", file );
+                        props.store( stream, "NOTE: This is a Maven Resolver internal implementation file"
+                            + ", its format can be changed without prior notice." );
+
+                        raf.seek( 0 );
+                        raf.write( stream.toByteArray() );
+                        raf.setLength( raf.getFilePointer() );
+                    }
+                    catch ( IOException e )
+                    {
+                        LOGGER.warn( "Failed to write tracking file {}", file, e );
+                    }
+                    finally
+                    {
+                        close( raf, file );
+                    }
+
+                    return props;
                 }
-                else
+                finally
                 {
-                    props.setProperty( update.getKey(), update.getValue() );
+                    lock.unlock();
                 }
             }
-
-            ByteArrayOutputStream stream = new ByteArrayOutputStream( 1024 * 2 );
-
-            LOGGER.debug( "Writing tracking file {}", file );
-            props.store( stream, "NOTE: This is a Maven Resolver internal implementation file"
-                + ", its format can be changed without prior notice." );
-
-            raf.seek( 0 );
-            raf.write( stream.toByteArray() );
-            raf.setLength( raf.getFilePointer() );
-        }
-        catch ( IOException e )
-        {
-            LOGGER.warn( "Failed to write tracking file {}", file, e );
+            else
+            {
+                throw new IllegalStateException( "Could not acquire write lock for: " + file );
+            }
         }
-        finally
+        catch ( InterruptedException e )
         {
-            close( raf, file );
+            throw new IllegalStateException( e );
         }
-
-        return props;
     }
 
     private void close( Closeable closeable, File file )
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
index 3c27f98..13f3de5 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
@@ -38,8 +38,7 @@ import org.eclipse.aether.artifact.ArtifactProperties;
 import org.eclipse.aether.artifact.DefaultArtifact;
 import org.eclipse.aether.impl.UpdateCheckManager;
 import org.eclipse.aether.impl.VersionResolver;
-import org.eclipse.aether.internal.impl.DefaultArtifactResolver;
-import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager;
+import org.eclipse.aether.internal.impl.synccontext.NamedLockFactorySelector;
 import org.eclipse.aether.internal.test.util.TestFileProcessor;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager;
@@ -274,7 +273,7 @@ public class DefaultArtifactResolverTest
         repositoryConnectorProvider.setConnector( connector );
         resolver.setUpdateCheckManager( new DefaultUpdateCheckManager()
             .setUpdatePolicyAnalyzer( new DefaultUpdatePolicyAnalyzer() )
-            .setTrackingFileManager( new DefaultTrackingFileManager() )
+            .setTrackingFileManager( new DefaultTrackingFileManager( new NamedLockFactorySelector() ) )
         );
 
         session.setResolutionErrorPolicy( new SimpleResolutionErrorPolicy( true, false ) );
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManagerTest.java
index e3745aa..ac241cb 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManagerTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManagerTest.java
@@ -23,14 +23,11 @@ import static org.junit.Assert.*;
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
-import org.eclipse.aether.internal.impl.TrackingFileManager;
+import org.eclipse.aether.internal.impl.synccontext.NamedLockFactorySelector;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.junit.Test;
 
@@ -38,13 +35,12 @@ import org.junit.Test;
  */
 public class DefaultTrackingFileManagerTest
 {
+    private final TrackingFileManager tfm = new DefaultTrackingFileManager( new NamedLockFactorySelector() );
 
     @Test
     public void testRead()
         throws Exception
     {
-        TrackingFileManager tfm = new DefaultTrackingFileManager();
-
         File propFile = TestFileUtils.createTempFile( "#COMMENT\nkey1=value1\nkey2 : value2" );
         Properties props = tfm.read( propFile );
 
@@ -63,8 +59,6 @@ public class DefaultTrackingFileManagerTest
     public void testReadNoFileLeak()
         throws Exception
     {
-        TrackingFileManager tfm = new DefaultTrackingFileManager();
-
         for ( int i = 0; i < 1000; i++ )
         {
             File propFile = TestFileUtils.createTempFile( "#COMMENT\nkey1=value1\nkey2 : value2" );
@@ -77,8 +71,6 @@ public class DefaultTrackingFileManagerTest
     public void testUpdate()
         throws Exception
     {
-        TrackingFileManager tfm = new DefaultTrackingFileManager();
-
         // NOTE: The excessive repetitions are to check the update properly truncates the file
         File propFile = TestFileUtils.createTempFile( "key1=value1\nkey2 : value2\n".getBytes( StandardCharsets.UTF_8 ), 1000 );
 
@@ -100,8 +92,6 @@ public class DefaultTrackingFileManagerTest
     public void testUpdateNoFileLeak()
         throws Exception
     {
-        TrackingFileManager tfm = new DefaultTrackingFileManager();
-
         Map<String, String> updates = new HashMap<>();
         updates.put( "k", "v" );
 
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManagerTest.java
index 111aca7..e9261b6 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManagerTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManagerTest.java
@@ -32,7 +32,7 @@ import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.artifact.DefaultArtifact;
 import org.eclipse.aether.impl.UpdateCheck;
-import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager;
+import org.eclipse.aether.internal.impl.synccontext.NamedLockFactorySelector;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.eclipse.aether.internal.test.util.TestUtils;
 import org.eclipse.aether.metadata.DefaultMetadata;
@@ -82,7 +82,7 @@ public class DefaultUpdateCheckManagerTest
             new RemoteRepository.Builder( "id", "default", TestFileUtils.createTempDir().toURI().toURL().toString() ).build();
         manager = new DefaultUpdateCheckManager()
             .setUpdatePolicyAnalyzer( new DefaultUpdatePolicyAnalyzer() )
-            .setTrackingFileManager( new DefaultTrackingFileManager() );
+            .setTrackingFileManager( new DefaultTrackingFileManager( new NamedLockFactorySelector() ) );
         metadata =
             new DefaultMetadata( "gid", "aid", "ver", "maven-metadata.xml", Metadata.Nature.RELEASE_OR_SNAPSHOT,
                                  metadataFile );
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java
index 1dae789..598e712 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java
@@ -31,7 +31,7 @@ import java.util.Collections;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.artifact.DefaultArtifact;
-import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManager;
+import org.eclipse.aether.internal.impl.synccontext.NamedLockFactorySelector;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.eclipse.aether.internal.test.util.TestUtils;
 import org.eclipse.aether.metadata.DefaultMetadata;
@@ -98,7 +98,7 @@ public class EnhancedLocalRepositoryManagerTest
 
         basedir = TestFileUtils.createTempDir( "enhanced-repo" );
         session = TestUtils.newSession();
-        trackingFileManager = new DefaultTrackingFileManager();
+        trackingFileManager = new DefaultTrackingFileManager( new NamedLockFactorySelector() );
         manager = new EnhancedLocalRepositoryManager( basedir, session, trackingFileManager );
 
         artifactFile = new File( basedir, manager.getPathForLocalArtifact( artifact ) );