From 70bbe7078244aaa5faf51a76a8d669c21dc83b4d Mon Sep 17 00:00:00 2001 From: "Edwin L. Punzalan" Date: Tue, 13 Dec 2005 07:52:22 +0000 Subject: [PATCH] PR: MRM-39 Added Cache class, and revised CachedRepositoryQueryLayer to use it. git-svn-id: https://svn.apache.org/repos/asf/maven/repository-manager/trunk@356503 13f79535-47bb-0310-9956-ffa450edef68 --- .../AbstractRepositoryQueryLayer.java | 2 +- .../maven/repository/reporting/Cache.java | 190 ++++++++++++++++++ .../reporting/CachedRepositoryQueryLayer.java | 79 ++------ .../AbstractRepositoryQueryLayerTest.java | 1 - .../maven/repository/reporting/CacheTest.java | 105 ++++++++++ .../CachedRepositoryQueryLayerTest.java | 12 +- 6 files changed, 323 insertions(+), 66 deletions(-) create mode 100644 maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/Cache.java create mode 100644 maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/CacheTest.java diff --git a/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/AbstractRepositoryQueryLayer.java b/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/AbstractRepositoryQueryLayer.java index f6fb93645..7f2d4e43c 100644 --- a/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/AbstractRepositoryQueryLayer.java +++ b/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/AbstractRepositoryQueryLayer.java @@ -60,7 +60,7 @@ public abstract class AbstractRepositoryQueryLayer protected String getSnapshotArtifactRepositoryPath( Artifact artifact, Snapshot snapshot ) { File f = new File( repository.getBasedir(), repository.pathOf( artifact ) ); - String snapshotInfo = artifact.getVersion().replaceAll( "SNAPSHOT", snapshot.getTimestamp() + "-" + + String snapshotInfo = artifact.getVersion().replaceFirst( "SNAPSHOT", snapshot.getTimestamp() + "-" + snapshot.getBuildNumber() + ".pom" ); File snapshotFile = new File( f.getParentFile(), artifact.getArtifactId() + "-" + snapshotInfo ); return snapshotFile.getAbsolutePath(); diff --git a/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/Cache.java b/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/Cache.java new file mode 100644 index 000000000..b7bd0ddc8 --- /dev/null +++ b/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/Cache.java @@ -0,0 +1,190 @@ +package org.apache.maven.repository.reporting; + +/* + * Copyright 2001-2005 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 java.util.HashMap; +import java.util.Map; + +/** + * + */ +public class Cache +{ + private Map cache; + + private DblLinkedList mostRecent; + private double cacheHitRatio = 0; + private long cacheMaxSize = 0; + + private long cacheHits = 0; + private long cacheMiss = 0; + + public Cache( double cacheHitRatio ) + { + this( cacheHitRatio, 0 ); + } + + public Cache( double cacheHitRatio, long cacheMaxSize ) + { + this.cacheHitRatio = cacheHitRatio; + this.cacheMaxSize = cacheMaxSize; + + cache = new HashMap(); + } + + public Object get( Object key ) + { + Object retValue = null; + + if ( cache.containsKey( key ) ) + { + DblLinkedList cacheEntry = (DblLinkedList) cache.get( key ); + + makeMostRecent( cacheEntry ); + + retValue = cacheEntry.cacheValue; + + cacheHits++; + } + else + { + cacheMiss++; + } + + return retValue; + } + + public void put( Object key, Object value ) + { + DblLinkedList entry; + if ( !cache.containsKey( key ) ) + { + entry = new DblLinkedList( key, value ); + cache.put( key, entry ); + manageCache(); + } + else + { + entry = (DblLinkedList) cache.get( key ); + } + + makeMostRecent( entry ); + } + + public double getHitRate() + { + return ( cacheHits == 0 && cacheMiss == 0 ) ? 0 : ( (double) cacheHits ) / (double) ( cacheHits + cacheMiss ); + } + + public long size() + { + return cache.size(); + } + + public void flush() + { + while ( cache.size() > 0 ) + trimCache(); + cacheHits = 0; + cacheMiss = 0; + cache = new HashMap(); + } + + private void makeMostRecent( DblLinkedList list ) + { + if ( mostRecent != list ) + { + removeFromLinks( list ); + + if ( mostRecent != null ) + { + list.next = mostRecent; + mostRecent.prev = list; + } + mostRecent = list; + } + } + + private void removeFromLinks( DblLinkedList list ) + { + if ( list.prev != null ) + list.prev.next = list.next; + if ( list.next != null ) + list.next.prev = list.prev; + + list.prev = null; + list.next = null; + } + + private void manageCache() + { + if ( cacheMaxSize == 0 ) + { + //if desired HitRatio is reached, we can trim the cache to conserve memory + if ( cacheHitRatio <= getHitRate() ) + { + trimCache(); + } + } + else if ( cache.size() > cacheMaxSize ) + { + //trim cache regardless of cacheHitRatio + while( cache.size() > cacheMaxSize ) + { + trimCache(); + } + } + } + + private void trimCache() + { + DblLinkedList leastRecent = getLeastRecent(); + cache.remove( leastRecent.cacheKey ); + if ( cache.size() > 0 ) + { + removeFromLinks( leastRecent ); + } + else + { + mostRecent = null; + } + } + + private DblLinkedList getLeastRecent() + { + DblLinkedList trail = mostRecent; + + while( trail.next != null ) + trail = trail.next; + + return trail; + } + + private class DblLinkedList { + Object cacheKey; + Object cacheValue; + DblLinkedList prev; + DblLinkedList next; + + public DblLinkedList( Object key, Object value ) + { + this.cacheKey = key; + this.cacheValue = value; + } + } +} diff --git a/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/CachedRepositoryQueryLayer.java b/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/CachedRepositoryQueryLayer.java index 2b98ab570..fa2485ff1 100644 --- a/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/CachedRepositoryQueryLayer.java +++ b/maven-repository-reports-standard/src/main/java/org/apache/maven/repository/reporting/CachedRepositoryQueryLayer.java @@ -18,17 +18,15 @@ package org.apache.maven.repository.reporting; */ import java.io.File; -import java.io.FileReader; import java.util.HashMap; -import java.util.List; +import java.util.LinkedList; import java.util.Map; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.ArtifactRepository; -import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata; import org.apache.maven.artifact.repository.metadata.Metadata; import org.apache.maven.artifact.repository.metadata.Snapshot; -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; + /** @@ -37,44 +35,34 @@ import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; public class CachedRepositoryQueryLayer extends AbstractRepositoryQueryLayer { - //@todo caches should expire - //cache for metadata - private Map cacheMetadata; + private Cache cache; - //cache for repository files, all types - //@todo ???should also cache missing files - private Map cacheFile; - //@todo ???should a listener be required??? - private long cacheHits = 0; - public CachedRepositoryQueryLayer( ArtifactRepository repository ) { this.repository = repository; - cacheMetadata = new HashMap(); - - cacheFile = new HashMap(); + cache = new Cache( 0.5 ); } - public long getCacheHits() + public double getCacheHitRate() { - return cacheHits; + return cache.getHitRate(); } - + public boolean containsArtifact( Artifact artifact ) { boolean artifactFound = true; // @todo should check for snapshot artifacts - File artifactFile = new File( repository.pathOf( artifact ) ); + String artifactPath = repository.getBasedir() + "/" + repository.pathOf( artifact ); - if ( !checkFileCache( artifactFile ) ) + if ( cache.get( artifactPath ) == null ) { artifactFound = super.containsArtifact( artifact ); if ( artifactFound ) { - addToFileCache( artifactFile ); + cache.put( artifactPath, artifactPath ); } } @@ -87,63 +75,38 @@ public class CachedRepositoryQueryLayer String path = getSnapshotArtifactRepositoryPath( artifact, snapshot ); - if ( !checkFileCache( path ) ) + if ( cache.get( path ) == null ) { artifactFound = super.containsArtifact( artifact, snapshot ); if ( artifactFound ) { - addToFileCache( new File( repository.getBasedir(), path ) ); + cache.put( path, path ); } } return artifactFound; } - /** - * Method to utilize the cache - */ - private boolean checkFileCache( File file ) - { - boolean existing = false; - - if ( cacheFile.containsKey( file ) ) - { - cacheHits++; - existing = true; - } - - return existing; - } - - private boolean checkFileCache( String repositoryPath ) - { - return checkFileCache( new File( repository.getBasedir(), repositoryPath ) ); - } - - private void addToFileCache( File file ) - { - cacheFile.put( file, file ); - } - /** * Override method to utilize the cache */ protected Metadata getMetadata( Artifact artifact ) throws RepositoryQueryLayerException { - Metadata metadata = null; + Metadata metadata = (Metadata) cache.get( artifact.getId() ); - if ( cacheMetadata.containsKey( artifact.getId() ) ) - { - cacheHits++; - metadata = (Metadata) cacheMetadata.get( artifact.getId() ); - } - else + if ( metadata == null ) { metadata = super.getMetadata( artifact ); - cacheMetadata.put( artifact.getId(), metadata ); + cache.put( artifact.getId(), metadata ); } return metadata; } + + private class DblLinkedList { + DblLinkedList prev; + Object cacheObject; + DblLinkedList next; + } } diff --git a/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/AbstractRepositoryQueryLayerTest.java b/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/AbstractRepositoryQueryLayerTest.java index b6be1f538..b1633a826 100644 --- a/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/AbstractRepositoryQueryLayerTest.java +++ b/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/AbstractRepositoryQueryLayerTest.java @@ -129,7 +129,6 @@ public abstract class AbstractRepositoryQueryLayerTest protected Artifact getArtifact( String groupId, String artifactId, String version ) { - if ( artifactFactory == null ) System.out.println("NULL"); return artifactFactory.createBuildArtifact( groupId, artifactId, version, "pom" ); } diff --git a/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/CacheTest.java b/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/CacheTest.java new file mode 100644 index 000000000..878819d61 --- /dev/null +++ b/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/CacheTest.java @@ -0,0 +1,105 @@ +package org.apache.maven.repository.reporting; + +/* + * Copyright 2001-2005 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 junit.framework.TestCase; + +/** + * + */ +public class CacheTest + extends TestCase +{ + private Cache cache; + + public void testCacheManagementBasedOnHitsRatio() + { + cache = new Cache( 0.5 ); + newCacheObjectTests(); + + String key = "key"; + String value = "value"; + for( int ctr=1; ctr<10; ctr++ ) + { + cache.put( key + ctr, value + ctr ); + } + + while ( cache.getHitRate() < 0.75 ) + { + cache.get( "key2" ); + } + cache.put( "key10", "value10"); + assertNull( "first key must be expired", cache.get( "key1" ) ); + } + + public void testCacheManagementBasedOnCacheSize() + { + cache = new Cache( 0.5, 9 ); + newCacheObjectTests(); + + String key = "key"; + String value = "value"; + for( int ctr=1; ctr<10; ctr++ ) + { + cache.put( key + ctr, value + ctr ); + } + + cache.put( "key10", "value10"); + assertNull( "first key must be expired", cache.get( "key1" ) ); + assertEquals( "check cache size to be max size", 9, cache.size() ); + } + + public void testCacheOnRedundantData() + { + cache = new Cache( 0.5, 9 ); + newCacheObjectTests(); + + String key = "key"; + String value = "value"; + for( int ctr=1; ctr<10; ctr++ ) + { + cache.put( key + ctr, value + ctr ); + } + + cache.put( "key1", "value1"); + cache.put( "key10", "value10"); + assertNull( "second key must be gone", cache.get( "key2" ) ); + assertEquals( "check cache size to be max size", 9, cache.size() ); + } + + private void newCacheObjectTests() + { + assertEquals( (double) 0, cache.getHitRate(), 0 ); + assertEquals( "check cache size", 0, cache.size() ); + + String value = "value"; + String key = "key"; + + cache.put( key, value ); + assertEquals( "check cache hit", value, cache.get( key ) ); + assertEquals( (double) 1, cache.getHitRate(), 0 ); + assertEquals( "check cache size", 1, cache.size() ); + assertNull( "check cache miss", cache.get( "none" ) ); + assertEquals( (double) 0.5, cache.getHitRate(), 0 ); + cache.flush(); + assertNull( "check flushed object", cache.get( "key" ) ); + assertEquals( (double) 0, cache.getHitRate(), 0 ); + assertEquals( "check flushed cache size", 0, cache.size() ); + cache.flush(); + } +} diff --git a/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/CachedRepositoryQueryLayerTest.java b/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/CachedRepositoryQueryLayerTest.java index f6774b53a..984f07ddd 100644 --- a/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/CachedRepositoryQueryLayerTest.java +++ b/maven-repository-reports-standard/src/test/java/org/apache/maven/repository/reporting/CachedRepositoryQueryLayerTest.java @@ -33,25 +33,25 @@ public class CachedRepositoryQueryLayerTest public void testUseFileCache() { testContainsArtifactTrue(); - assertEquals( "check cache usage", 0, queryLayer.getCacheHits() ); + assertEquals( 0, queryLayer.getCacheHitRate(), 0 ); testContainsArtifactTrue(); - assertEquals( "check cache usage", 1, queryLayer.getCacheHits() ); + assertEquals( 0.50, queryLayer.getCacheHitRate(), 0 ); } public void testUseMetadataCache() throws Exception { testArtifactVersionsTrue(); - assertEquals( "check cache usage", 0, queryLayer.getCacheHits() ); + assertEquals( 0, queryLayer.getCacheHitRate(), 0 ); testArtifactVersionsTrue(); - assertEquals( "check cache usage", 1, queryLayer.getCacheHits() ); + assertEquals( 0.50, queryLayer.getCacheHitRate(), 0 ); } public void testUseFileCacheOnSnapshot() { testContainsSnapshotArtifactTrue(); - assertEquals( "check cache usage", 0, queryLayer.getCacheHits() ); + assertEquals( 0, queryLayer.getCacheHitRate(), 0 ); testContainsSnapshotArtifactTrue(); - assertEquals( "check cache usage", 1, queryLayer.getCacheHits() ); + assertEquals( 0.50, queryLayer.getCacheHitRate(), 0 ); } } -- 2.39.5